##// END OF EJS Templates
branching: merge with stable
Martin von Zweigbergk -
r47244:d67732a4 merge default
parent child Browse files
Show More

The requested changes are too big and content was truncated. Show full diff

@@ -1,1864 +1,1864 b''
1 1 # Copyright 2009-2010 Gregory P. Ward
2 2 # Copyright 2009-2010 Intelerad Medical Systems Incorporated
3 3 # Copyright 2010-2011 Fog Creek Software
4 4 # Copyright 2010-2011 Unity Technologies
5 5 #
6 6 # This software may be used and distributed according to the terms of the
7 7 # GNU General Public License version 2 or any later version.
8 8
9 9 '''Overridden Mercurial commands and functions for the largefiles extension'''
10 10 from __future__ import absolute_import
11 11
12 12 import copy
13 13 import os
14 14
15 15 from mercurial.i18n import _
16 16
17 17 from mercurial.pycompat import open
18 18
19 19 from mercurial.hgweb import webcommands
20 20
21 21 from mercurial import (
22 22 archival,
23 23 cmdutil,
24 24 copies as copiesmod,
25 25 error,
26 26 exchange,
27 27 extensions,
28 28 exthelper,
29 29 filemerge,
30 30 hg,
31 31 logcmdutil,
32 32 match as matchmod,
33 33 merge,
34 34 mergestate as mergestatemod,
35 35 pathutil,
36 36 pycompat,
37 37 scmutil,
38 38 smartset,
39 39 subrepo,
40 40 url as urlmod,
41 41 util,
42 42 )
43 43
44 44 from mercurial.upgrade_utils import (
45 45 actions as upgrade_actions,
46 46 )
47 47
48 48 from . import (
49 49 lfcommands,
50 50 lfutil,
51 51 storefactory,
52 52 )
53 53
54 54 eh = exthelper.exthelper()
55 55
56 56 lfstatus = lfutil.lfstatus
57 57
58 58 MERGE_ACTION_LARGEFILE_MARK_REMOVED = b'lfmr'
59 59
60 60 # -- Utility functions: commonly/repeatedly needed functionality ---------------
61 61
62 62
63 63 def composelargefilematcher(match, manifest):
64 64 """create a matcher that matches only the largefiles in the original
65 65 matcher"""
66 66 m = copy.copy(match)
67 67 lfile = lambda f: lfutil.standin(f) in manifest
68 68 m._files = [lf for lf in m._files if lfile(lf)]
69 69 m._fileset = set(m._files)
70 70 m.always = lambda: False
71 71 origmatchfn = m.matchfn
72 72 m.matchfn = lambda f: lfile(f) and origmatchfn(f)
73 73 return m
74 74
75 75
76 76 def composenormalfilematcher(match, manifest, exclude=None):
77 77 excluded = set()
78 78 if exclude is not None:
79 79 excluded.update(exclude)
80 80
81 81 m = copy.copy(match)
82 82 notlfile = lambda f: not (
83 83 lfutil.isstandin(f) or lfutil.standin(f) in manifest or f in excluded
84 84 )
85 85 m._files = [lf for lf in m._files if notlfile(lf)]
86 86 m._fileset = set(m._files)
87 87 m.always = lambda: False
88 88 origmatchfn = m.matchfn
89 89 m.matchfn = lambda f: notlfile(f) and origmatchfn(f)
90 90 return m
91 91
92 92
93 93 def addlargefiles(ui, repo, isaddremove, matcher, uipathfn, **opts):
94 94 large = opts.get('large')
95 95 lfsize = lfutil.getminsize(
96 96 ui, lfutil.islfilesrepo(repo), opts.get('lfsize')
97 97 )
98 98
99 99 lfmatcher = None
100 100 if lfutil.islfilesrepo(repo):
101 101 lfpats = ui.configlist(lfutil.longname, b'patterns')
102 102 if lfpats:
103 103 lfmatcher = matchmod.match(repo.root, b'', list(lfpats))
104 104
105 105 lfnames = []
106 106 m = matcher
107 107
108 108 wctx = repo[None]
109 109 for f in wctx.walk(matchmod.badmatch(m, lambda x, y: None)):
110 110 exact = m.exact(f)
111 111 lfile = lfutil.standin(f) in wctx
112 112 nfile = f in wctx
113 113 exists = lfile or nfile
114 114
115 115 # Don't warn the user when they attempt to add a normal tracked file.
116 116 # The normal add code will do that for us.
117 117 if exact and exists:
118 118 if lfile:
119 119 ui.warn(_(b'%s already a largefile\n') % uipathfn(f))
120 120 continue
121 121
122 122 if (exact or not exists) and not lfutil.isstandin(f):
123 123 # In case the file was removed previously, but not committed
124 124 # (issue3507)
125 125 if not repo.wvfs.exists(f):
126 126 continue
127 127
128 128 abovemin = (
129 129 lfsize and repo.wvfs.lstat(f).st_size >= lfsize * 1024 * 1024
130 130 )
131 131 if large or abovemin or (lfmatcher and lfmatcher(f)):
132 132 lfnames.append(f)
133 133 if ui.verbose or not exact:
134 134 ui.status(_(b'adding %s as a largefile\n') % uipathfn(f))
135 135
136 136 bad = []
137 137
138 138 # Need to lock, otherwise there could be a race condition between
139 139 # when standins are created and added to the repo.
140 140 with repo.wlock():
141 141 if not opts.get('dry_run'):
142 142 standins = []
143 143 lfdirstate = lfutil.openlfdirstate(ui, repo)
144 144 for f in lfnames:
145 145 standinname = lfutil.standin(f)
146 146 lfutil.writestandin(
147 147 repo,
148 148 standinname,
149 149 hash=b'',
150 150 executable=lfutil.getexecutable(repo.wjoin(f)),
151 151 )
152 152 standins.append(standinname)
153 153 if lfdirstate[f] == b'r':
154 154 lfdirstate.normallookup(f)
155 155 else:
156 156 lfdirstate.add(f)
157 157 lfdirstate.write()
158 158 bad += [
159 159 lfutil.splitstandin(f)
160 160 for f in repo[None].add(standins)
161 161 if f in m.files()
162 162 ]
163 163
164 164 added = [f for f in lfnames if f not in bad]
165 165 return added, bad
166 166
167 167
168 168 def removelargefiles(ui, repo, isaddremove, matcher, uipathfn, dryrun, **opts):
169 169 after = opts.get('after')
170 170 m = composelargefilematcher(matcher, repo[None].manifest())
171 171 with lfstatus(repo):
172 172 s = repo.status(match=m, clean=not isaddremove)
173 173 manifest = repo[None].manifest()
174 174 modified, added, deleted, clean = [
175 175 [f for f in list if lfutil.standin(f) in manifest]
176 176 for list in (s.modified, s.added, s.deleted, s.clean)
177 177 ]
178 178
179 179 def warn(files, msg):
180 180 for f in files:
181 181 ui.warn(msg % uipathfn(f))
182 182 return int(len(files) > 0)
183 183
184 184 if after:
185 185 remove = deleted
186 186 result = warn(
187 187 modified + added + clean, _(b'not removing %s: file still exists\n')
188 188 )
189 189 else:
190 190 remove = deleted + clean
191 191 result = warn(
192 192 modified,
193 193 _(
194 194 b'not removing %s: file is modified (use -f'
195 195 b' to force removal)\n'
196 196 ),
197 197 )
198 198 result = (
199 199 warn(
200 200 added,
201 201 _(
202 202 b'not removing %s: file has been marked for add'
203 203 b' (use forget to undo)\n'
204 204 ),
205 205 )
206 206 or result
207 207 )
208 208
209 209 # Need to lock because standin files are deleted then removed from the
210 210 # repository and we could race in-between.
211 211 with repo.wlock():
212 212 lfdirstate = lfutil.openlfdirstate(ui, repo)
213 213 for f in sorted(remove):
214 214 if ui.verbose or not m.exact(f):
215 215 ui.status(_(b'removing %s\n') % uipathfn(f))
216 216
217 217 if not dryrun:
218 218 if not after:
219 219 repo.wvfs.unlinkpath(f, ignoremissing=True)
220 220
221 221 if dryrun:
222 222 return result
223 223
224 224 remove = [lfutil.standin(f) for f in remove]
225 225 # If this is being called by addremove, let the original addremove
226 226 # function handle this.
227 227 if not isaddremove:
228 228 for f in remove:
229 229 repo.wvfs.unlinkpath(f, ignoremissing=True)
230 230 repo[None].forget(remove)
231 231
232 232 for f in remove:
233 233 lfutil.synclfdirstate(
234 234 repo, lfdirstate, lfutil.splitstandin(f), False
235 235 )
236 236
237 237 lfdirstate.write()
238 238
239 239 return result
240 240
241 241
242 242 # For overriding mercurial.hgweb.webcommands so that largefiles will
243 243 # appear at their right place in the manifests.
244 244 @eh.wrapfunction(webcommands, b'decodepath')
245 245 def decodepath(orig, path):
246 246 return lfutil.splitstandin(path) or path
247 247
248 248
249 249 # -- Wrappers: modify existing commands --------------------------------
250 250
251 251
252 252 @eh.wrapcommand(
253 253 b'add',
254 254 opts=[
255 255 (b'', b'large', None, _(b'add as largefile')),
256 256 (b'', b'normal', None, _(b'add as normal file')),
257 257 (
258 258 b'',
259 259 b'lfsize',
260 260 b'',
261 261 _(
262 262 b'add all files above this size (in megabytes) '
263 263 b'as largefiles (default: 10)'
264 264 ),
265 265 ),
266 266 ],
267 267 )
268 268 def overrideadd(orig, ui, repo, *pats, **opts):
269 269 if opts.get('normal') and opts.get('large'):
270 270 raise error.Abort(_(b'--normal cannot be used with --large'))
271 271 return orig(ui, repo, *pats, **opts)
272 272
273 273
274 274 @eh.wrapfunction(cmdutil, b'add')
275 275 def cmdutiladd(orig, ui, repo, matcher, prefix, uipathfn, explicitonly, **opts):
276 276 # The --normal flag short circuits this override
277 277 if opts.get('normal'):
278 278 return orig(ui, repo, matcher, prefix, uipathfn, explicitonly, **opts)
279 279
280 280 ladded, lbad = addlargefiles(ui, repo, False, matcher, uipathfn, **opts)
281 281 normalmatcher = composenormalfilematcher(
282 282 matcher, repo[None].manifest(), ladded
283 283 )
284 284 bad = orig(ui, repo, normalmatcher, prefix, uipathfn, explicitonly, **opts)
285 285
286 286 bad.extend(f for f in lbad)
287 287 return bad
288 288
289 289
290 290 @eh.wrapfunction(cmdutil, b'remove')
291 291 def cmdutilremove(
292 292 orig, ui, repo, matcher, prefix, uipathfn, after, force, subrepos, dryrun
293 293 ):
294 294 normalmatcher = composenormalfilematcher(matcher, repo[None].manifest())
295 295 result = orig(
296 296 ui,
297 297 repo,
298 298 normalmatcher,
299 299 prefix,
300 300 uipathfn,
301 301 after,
302 302 force,
303 303 subrepos,
304 304 dryrun,
305 305 )
306 306 return (
307 307 removelargefiles(
308 308 ui, repo, False, matcher, uipathfn, dryrun, after=after, force=force
309 309 )
310 310 or result
311 311 )
312 312
313 313
314 314 @eh.wrapfunction(subrepo.hgsubrepo, b'status')
315 315 def overridestatusfn(orig, repo, rev2, **opts):
316 316 with lfstatus(repo._repo):
317 317 return orig(repo, rev2, **opts)
318 318
319 319
320 320 @eh.wrapcommand(b'status')
321 321 def overridestatus(orig, ui, repo, *pats, **opts):
322 322 with lfstatus(repo):
323 323 return orig(ui, repo, *pats, **opts)
324 324
325 325
326 326 @eh.wrapfunction(subrepo.hgsubrepo, b'dirty')
327 327 def overridedirty(orig, repo, ignoreupdate=False, missing=False):
328 328 with lfstatus(repo._repo):
329 329 return orig(repo, ignoreupdate=ignoreupdate, missing=missing)
330 330
331 331
332 332 @eh.wrapcommand(b'log')
333 333 def overridelog(orig, ui, repo, *pats, **opts):
334 334 def overridematchandpats(
335 335 orig,
336 336 ctx,
337 337 pats=(),
338 338 opts=None,
339 339 globbed=False,
340 340 default=b'relpath',
341 341 badfn=None,
342 342 ):
343 343 """Matcher that merges root directory with .hglf, suitable for log.
344 344 It is still possible to match .hglf directly.
345 345 For any listed files run log on the standin too.
346 346 matchfn tries both the given filename and with .hglf stripped.
347 347 """
348 348 if opts is None:
349 349 opts = {}
350 350 matchandpats = orig(ctx, pats, opts, globbed, default, badfn=badfn)
351 351 m, p = copy.copy(matchandpats)
352 352
353 353 if m.always():
354 354 # We want to match everything anyway, so there's no benefit trying
355 355 # to add standins.
356 356 return matchandpats
357 357
358 358 pats = set(p)
359 359
360 360 def fixpats(pat, tostandin=lfutil.standin):
361 361 if pat.startswith(b'set:'):
362 362 return pat
363 363
364 364 kindpat = matchmod._patsplit(pat, None)
365 365
366 366 if kindpat[0] is not None:
367 367 return kindpat[0] + b':' + tostandin(kindpat[1])
368 368 return tostandin(kindpat[1])
369 369
370 370 cwd = repo.getcwd()
371 371 if cwd:
372 372 hglf = lfutil.shortname
373 373 back = util.pconvert(repo.pathto(hglf)[: -len(hglf)])
374 374
375 375 def tostandin(f):
376 376 # The file may already be a standin, so truncate the back
377 377 # prefix and test before mangling it. This avoids turning
378 378 # 'glob:../.hglf/foo*' into 'glob:../.hglf/../.hglf/foo*'.
379 379 if f.startswith(back) and lfutil.splitstandin(f[len(back) :]):
380 380 return f
381 381
382 382 # An absolute path is from outside the repo, so truncate the
383 383 # path to the root before building the standin. Otherwise cwd
384 384 # is somewhere in the repo, relative to root, and needs to be
385 385 # prepended before building the standin.
386 386 if os.path.isabs(cwd):
387 387 f = f[len(back) :]
388 388 else:
389 389 f = cwd + b'/' + f
390 390 return back + lfutil.standin(f)
391 391
392 392 else:
393 393
394 394 def tostandin(f):
395 395 if lfutil.isstandin(f):
396 396 return f
397 397 return lfutil.standin(f)
398 398
399 399 pats.update(fixpats(f, tostandin) for f in p)
400 400
401 401 for i in range(0, len(m._files)):
402 402 # Don't add '.hglf' to m.files, since that is already covered by '.'
403 403 if m._files[i] == b'.':
404 404 continue
405 405 standin = lfutil.standin(m._files[i])
406 406 # If the "standin" is a directory, append instead of replace to
407 407 # support naming a directory on the command line with only
408 408 # largefiles. The original directory is kept to support normal
409 409 # files.
410 410 if standin in ctx:
411 411 m._files[i] = standin
412 412 elif m._files[i] not in ctx and repo.wvfs.isdir(standin):
413 413 m._files.append(standin)
414 414
415 415 m._fileset = set(m._files)
416 416 m.always = lambda: False
417 417 origmatchfn = m.matchfn
418 418
419 419 def lfmatchfn(f):
420 420 lf = lfutil.splitstandin(f)
421 421 if lf is not None and origmatchfn(lf):
422 422 return True
423 423 r = origmatchfn(f)
424 424 return r
425 425
426 426 m.matchfn = lfmatchfn
427 427
428 428 ui.debug(b'updated patterns: %s\n' % b', '.join(sorted(pats)))
429 429 return m, pats
430 430
431 431 # For hg log --patch, the match object is used in two different senses:
432 432 # (1) to determine what revisions should be printed out, and
433 433 # (2) to determine what files to print out diffs for.
434 434 # The magic matchandpats override should be used for case (1) but not for
435 435 # case (2).
436 436 oldmatchandpats = scmutil.matchandpats
437 437
438 438 def overridemakefilematcher(orig, repo, pats, opts, badfn=None):
439 439 wctx = repo[None]
440 440 match, pats = oldmatchandpats(wctx, pats, opts, badfn=badfn)
441 441 return lambda ctx: match
442 442
443 443 wrappedmatchandpats = extensions.wrappedfunction(
444 444 scmutil, b'matchandpats', overridematchandpats
445 445 )
446 446 wrappedmakefilematcher = extensions.wrappedfunction(
447 447 logcmdutil, b'_makenofollowfilematcher', overridemakefilematcher
448 448 )
449 449 with wrappedmatchandpats, wrappedmakefilematcher:
450 450 return orig(ui, repo, *pats, **opts)
451 451
452 452
453 453 @eh.wrapcommand(
454 454 b'verify',
455 455 opts=[
456 456 (
457 457 b'',
458 458 b'large',
459 459 None,
460 460 _(b'verify that all largefiles in current revision exists'),
461 461 ),
462 462 (
463 463 b'',
464 464 b'lfa',
465 465 None,
466 466 _(b'verify largefiles in all revisions, not just current'),
467 467 ),
468 468 (
469 469 b'',
470 470 b'lfc',
471 471 None,
472 472 _(b'verify local largefile contents, not just existence'),
473 473 ),
474 474 ],
475 475 )
476 476 def overrideverify(orig, ui, repo, *pats, **opts):
477 477 large = opts.pop('large', False)
478 478 all = opts.pop('lfa', False)
479 479 contents = opts.pop('lfc', False)
480 480
481 481 result = orig(ui, repo, *pats, **opts)
482 482 if large or all or contents:
483 483 result = result or lfcommands.verifylfiles(ui, repo, all, contents)
484 484 return result
485 485
486 486
487 487 @eh.wrapcommand(
488 488 b'debugstate',
489 489 opts=[(b'', b'large', None, _(b'display largefiles dirstate'))],
490 490 )
491 491 def overridedebugstate(orig, ui, repo, *pats, **opts):
492 492 large = opts.pop('large', False)
493 493 if large:
494 494
495 495 class fakerepo(object):
496 496 dirstate = lfutil.openlfdirstate(ui, repo)
497 497
498 498 orig(ui, fakerepo, *pats, **opts)
499 499 else:
500 500 orig(ui, repo, *pats, **opts)
501 501
502 502
503 503 # Before starting the manifest merge, merge.updates will call
504 504 # _checkunknownfile to check if there are any files in the merged-in
505 505 # changeset that collide with unknown files in the working copy.
506 506 #
507 507 # The largefiles are seen as unknown, so this prevents us from merging
508 508 # in a file 'foo' if we already have a largefile with the same name.
509 509 #
510 510 # The overridden function filters the unknown files by removing any
511 511 # largefiles. This makes the merge proceed and we can then handle this
512 512 # case further in the overridden calculateupdates function below.
513 513 @eh.wrapfunction(merge, b'_checkunknownfile')
514 514 def overridecheckunknownfile(origfn, repo, wctx, mctx, f, f2=None):
515 515 if lfutil.standin(repo.dirstate.normalize(f)) in wctx:
516 516 return False
517 517 return origfn(repo, wctx, mctx, f, f2)
518 518
519 519
520 520 # The manifest merge handles conflicts on the manifest level. We want
521 521 # to handle changes in largefile-ness of files at this level too.
522 522 #
523 523 # The strategy is to run the original calculateupdates and then process
524 524 # the action list it outputs. There are two cases we need to deal with:
525 525 #
526 526 # 1. Normal file in p1, largefile in p2. Here the largefile is
527 527 # detected via its standin file, which will enter the working copy
528 528 # with a "get" action. It is not "merge" since the standin is all
529 529 # Mercurial is concerned with at this level -- the link to the
530 530 # existing normal file is not relevant here.
531 531 #
532 532 # 2. Largefile in p1, normal file in p2. Here we get a "merge" action
533 533 # since the largefile will be present in the working copy and
534 534 # different from the normal file in p2. Mercurial therefore
535 535 # triggers a merge action.
536 536 #
537 537 # In both cases, we prompt the user and emit new actions to either
538 538 # remove the standin (if the normal file was kept) or to remove the
539 539 # normal file and get the standin (if the largefile was kept). The
540 540 # default prompt answer is to use the largefile version since it was
541 541 # presumably changed on purpose.
542 542 #
543 543 # Finally, the merge.applyupdates function will then take care of
544 544 # writing the files into the working copy and lfcommands.updatelfiles
545 545 # will update the largefiles.
546 546 @eh.wrapfunction(merge, b'calculateupdates')
547 547 def overridecalculateupdates(
548 548 origfn, repo, p1, p2, pas, branchmerge, force, acceptremote, *args, **kwargs
549 549 ):
550 550 overwrite = force and not branchmerge
551 551 mresult = origfn(
552 552 repo, p1, p2, pas, branchmerge, force, acceptremote, *args, **kwargs
553 553 )
554 554
555 555 if overwrite:
556 556 return mresult
557 557
558 558 # Convert to dictionary with filename as key and action as value.
559 559 lfiles = set()
560 560 for f in mresult.files():
561 561 splitstandin = lfutil.splitstandin(f)
562 562 if splitstandin is not None and splitstandin in p1:
563 563 lfiles.add(splitstandin)
564 564 elif lfutil.standin(f) in p1:
565 565 lfiles.add(f)
566 566
567 567 for lfile in sorted(lfiles):
568 568 standin = lfutil.standin(lfile)
569 569 (lm, largs, lmsg) = mresult.getfile(lfile, (None, None, None))
570 570 (sm, sargs, smsg) = mresult.getfile(standin, (None, None, None))
571 571 if sm in (b'g', b'dc') and lm != b'r':
572 572 if sm == b'dc':
573 573 f1, f2, fa, move, anc = sargs
574 574 sargs = (p2[f2].flags(), False)
575 575 # Case 1: normal file in the working copy, largefile in
576 576 # the second parent
577 577 usermsg = (
578 578 _(
579 579 b'remote turned local normal file %s into a largefile\n'
580 580 b'use (l)argefile or keep (n)ormal file?'
581 581 b'$$ &Largefile $$ &Normal file'
582 582 )
583 583 % lfile
584 584 )
585 585 if repo.ui.promptchoice(usermsg, 0) == 0: # pick remote largefile
586 586 mresult.addfile(lfile, b'r', None, b'replaced by standin')
587 587 mresult.addfile(standin, b'g', sargs, b'replaces standin')
588 588 else: # keep local normal file
589 589 mresult.addfile(lfile, b'k', None, b'replaces standin')
590 590 if branchmerge:
591 591 mresult.addfile(
592 592 standin,
593 593 b'k',
594 594 None,
595 595 b'replaced by non-standin',
596 596 )
597 597 else:
598 598 mresult.addfile(
599 599 standin,
600 600 b'r',
601 601 None,
602 602 b'replaced by non-standin',
603 603 )
604 604 elif lm in (b'g', b'dc') and sm != b'r':
605 605 if lm == b'dc':
606 606 f1, f2, fa, move, anc = largs
607 607 largs = (p2[f2].flags(), False)
608 608 # Case 2: largefile in the working copy, normal file in
609 609 # the second parent
610 610 usermsg = (
611 611 _(
612 612 b'remote turned local largefile %s into a normal file\n'
613 613 b'keep (l)argefile or use (n)ormal file?'
614 614 b'$$ &Largefile $$ &Normal file'
615 615 )
616 616 % lfile
617 617 )
618 618 if repo.ui.promptchoice(usermsg, 0) == 0: # keep local largefile
619 619 if branchmerge:
620 620 # largefile can be restored from standin safely
621 621 mresult.addfile(
622 622 lfile,
623 623 b'k',
624 624 None,
625 625 b'replaced by standin',
626 626 )
627 627 mresult.addfile(standin, b'k', None, b'replaces standin')
628 628 else:
629 629 # "lfile" should be marked as "removed" without
630 630 # removal of itself
631 631 mresult.addfile(
632 632 lfile,
633 633 MERGE_ACTION_LARGEFILE_MARK_REMOVED,
634 634 None,
635 635 b'forget non-standin largefile',
636 636 )
637 637
638 638 # linear-merge should treat this largefile as 're-added'
639 639 mresult.addfile(standin, b'a', None, b'keep standin')
640 640 else: # pick remote normal file
641 641 mresult.addfile(lfile, b'g', largs, b'replaces standin')
642 642 mresult.addfile(
643 643 standin,
644 644 b'r',
645 645 None,
646 646 b'replaced by non-standin',
647 647 )
648 648
649 649 return mresult
650 650
651 651
652 652 @eh.wrapfunction(mergestatemod, b'recordupdates')
653 653 def mergerecordupdates(orig, repo, actions, branchmerge, getfiledata):
654 654 if MERGE_ACTION_LARGEFILE_MARK_REMOVED in actions:
655 655 lfdirstate = lfutil.openlfdirstate(repo.ui, repo)
656 656 for lfile, args, msg in actions[MERGE_ACTION_LARGEFILE_MARK_REMOVED]:
657 657 # this should be executed before 'orig', to execute 'remove'
658 658 # before all other actions
659 659 repo.dirstate.remove(lfile)
660 660 # make sure lfile doesn't get synclfdirstate'd as normal
661 661 lfdirstate.add(lfile)
662 662 lfdirstate.write()
663 663
664 664 return orig(repo, actions, branchmerge, getfiledata)
665 665
666 666
667 667 # Override filemerge to prompt the user about how they wish to merge
668 668 # largefiles. This will handle identical edits without prompting the user.
669 669 @eh.wrapfunction(filemerge, b'_filemerge')
670 670 def overridefilemerge(
671 671 origfn, premerge, repo, wctx, mynode, orig, fcd, fco, fca, labels=None
672 672 ):
673 673 if not lfutil.isstandin(orig) or fcd.isabsent() or fco.isabsent():
674 674 return origfn(
675 675 premerge, repo, wctx, mynode, orig, fcd, fco, fca, labels=labels
676 676 )
677 677
678 678 ahash = lfutil.readasstandin(fca).lower()
679 679 dhash = lfutil.readasstandin(fcd).lower()
680 680 ohash = lfutil.readasstandin(fco).lower()
681 681 if (
682 682 ohash != ahash
683 683 and ohash != dhash
684 684 and (
685 685 dhash == ahash
686 686 or repo.ui.promptchoice(
687 687 _(
688 688 b'largefile %s has a merge conflict\nancestor was %s\n'
689 689 b'you can keep (l)ocal %s or take (o)ther %s.\n'
690 690 b'what do you want to do?'
691 691 b'$$ &Local $$ &Other'
692 692 )
693 693 % (lfutil.splitstandin(orig), ahash, dhash, ohash),
694 694 0,
695 695 )
696 696 == 1
697 697 )
698 698 ):
699 699 repo.wwrite(fcd.path(), fco.data(), fco.flags())
700 700 return True, 0, False
701 701
702 702
703 703 @eh.wrapfunction(copiesmod, b'pathcopies')
704 704 def copiespathcopies(orig, ctx1, ctx2, match=None):
705 705 copies = orig(ctx1, ctx2, match=match)
706 706 updated = {}
707 707
708 708 for k, v in pycompat.iteritems(copies):
709 709 updated[lfutil.splitstandin(k) or k] = lfutil.splitstandin(v) or v
710 710
711 711 return updated
712 712
713 713
714 714 # Copy first changes the matchers to match standins instead of
715 715 # largefiles. Then it overrides util.copyfile in that function it
716 716 # checks if the destination largefile already exists. It also keeps a
717 717 # list of copied files so that the largefiles can be copied and the
718 718 # dirstate updated.
719 719 @eh.wrapfunction(cmdutil, b'copy')
720 720 def overridecopy(orig, ui, repo, pats, opts, rename=False):
721 721 # doesn't remove largefile on rename
722 722 if len(pats) < 2:
723 723 # this isn't legal, let the original function deal with it
724 724 return orig(ui, repo, pats, opts, rename)
725 725
726 726 # This could copy both lfiles and normal files in one command,
727 727 # but we don't want to do that. First replace their matcher to
728 728 # only match normal files and run it, then replace it to just
729 729 # match largefiles and run it again.
730 730 nonormalfiles = False
731 731 nolfiles = False
732 732 manifest = repo[None].manifest()
733 733
734 734 def normalfilesmatchfn(
735 735 orig,
736 736 ctx,
737 737 pats=(),
738 738 opts=None,
739 739 globbed=False,
740 740 default=b'relpath',
741 741 badfn=None,
742 742 ):
743 743 if opts is None:
744 744 opts = {}
745 745 match = orig(ctx, pats, opts, globbed, default, badfn=badfn)
746 746 return composenormalfilematcher(match, manifest)
747 747
748 748 with extensions.wrappedfunction(scmutil, b'match', normalfilesmatchfn):
749 749 try:
750 750 result = orig(ui, repo, pats, opts, rename)
751 751 except error.Abort as e:
752 752 if e.message != _(b'no files to copy'):
753 753 raise e
754 754 else:
755 755 nonormalfiles = True
756 756 result = 0
757 757
758 758 # The first rename can cause our current working directory to be removed.
759 759 # In that case there is nothing left to copy/rename so just quit.
760 760 try:
761 761 repo.getcwd()
762 762 except OSError:
763 763 return result
764 764
765 765 def makestandin(relpath):
766 766 path = pathutil.canonpath(repo.root, repo.getcwd(), relpath)
767 767 return repo.wvfs.join(lfutil.standin(path))
768 768
769 769 fullpats = scmutil.expandpats(pats)
770 770 dest = fullpats[-1]
771 771
772 772 if os.path.isdir(dest):
773 773 if not os.path.isdir(makestandin(dest)):
774 774 os.makedirs(makestandin(dest))
775 775
776 776 try:
777 777 # When we call orig below it creates the standins but we don't add
778 778 # them to the dir state until later so lock during that time.
779 779 wlock = repo.wlock()
780 780
781 781 manifest = repo[None].manifest()
782 782
783 783 def overridematch(
784 784 orig,
785 785 ctx,
786 786 pats=(),
787 787 opts=None,
788 788 globbed=False,
789 789 default=b'relpath',
790 790 badfn=None,
791 791 ):
792 792 if opts is None:
793 793 opts = {}
794 794 newpats = []
795 795 # The patterns were previously mangled to add the standin
796 796 # directory; we need to remove that now
797 797 for pat in pats:
798 798 if matchmod.patkind(pat) is None and lfutil.shortname in pat:
799 799 newpats.append(pat.replace(lfutil.shortname, b''))
800 800 else:
801 801 newpats.append(pat)
802 802 match = orig(ctx, newpats, opts, globbed, default, badfn=badfn)
803 803 m = copy.copy(match)
804 804 lfile = lambda f: lfutil.standin(f) in manifest
805 805 m._files = [lfutil.standin(f) for f in m._files if lfile(f)]
806 806 m._fileset = set(m._files)
807 807 origmatchfn = m.matchfn
808 808
809 809 def matchfn(f):
810 810 lfile = lfutil.splitstandin(f)
811 811 return (
812 812 lfile is not None
813 813 and (f in manifest)
814 814 and origmatchfn(lfile)
815 815 or None
816 816 )
817 817
818 818 m.matchfn = matchfn
819 819 return m
820 820
821 821 listpats = []
822 822 for pat in pats:
823 823 if matchmod.patkind(pat) is not None:
824 824 listpats.append(pat)
825 825 else:
826 826 listpats.append(makestandin(pat))
827 827
828 828 copiedfiles = []
829 829
830 830 def overridecopyfile(orig, src, dest, *args, **kwargs):
831 831 if lfutil.shortname in src and dest.startswith(
832 832 repo.wjoin(lfutil.shortname)
833 833 ):
834 834 destlfile = dest.replace(lfutil.shortname, b'')
835 835 if not opts[b'force'] and os.path.exists(destlfile):
836 836 raise IOError(
837 837 b'', _(b'destination largefile already exists')
838 838 )
839 839 copiedfiles.append((src, dest))
840 840 orig(src, dest, *args, **kwargs)
841 841
842 842 with extensions.wrappedfunction(util, b'copyfile', overridecopyfile):
843 843 with extensions.wrappedfunction(scmutil, b'match', overridematch):
844 844 result += orig(ui, repo, listpats, opts, rename)
845 845
846 846 lfdirstate = lfutil.openlfdirstate(ui, repo)
847 847 for (src, dest) in copiedfiles:
848 848 if lfutil.shortname in src and dest.startswith(
849 849 repo.wjoin(lfutil.shortname)
850 850 ):
851 851 srclfile = src.replace(repo.wjoin(lfutil.standin(b'')), b'')
852 852 destlfile = dest.replace(repo.wjoin(lfutil.standin(b'')), b'')
853 853 destlfiledir = repo.wvfs.dirname(repo.wjoin(destlfile)) or b'.'
854 854 if not os.path.isdir(destlfiledir):
855 855 os.makedirs(destlfiledir)
856 856 if rename:
857 857 os.rename(repo.wjoin(srclfile), repo.wjoin(destlfile))
858 858
859 859 # The file is gone, but this deletes any empty parent
860 860 # directories as a side-effect.
861 861 repo.wvfs.unlinkpath(srclfile, ignoremissing=True)
862 862 lfdirstate.remove(srclfile)
863 863 else:
864 864 util.copyfile(repo.wjoin(srclfile), repo.wjoin(destlfile))
865 865
866 866 lfdirstate.add(destlfile)
867 867 lfdirstate.write()
868 868 except error.Abort as e:
869 869 if e.message != _(b'no files to copy'):
870 870 raise e
871 871 else:
872 872 nolfiles = True
873 873 finally:
874 874 wlock.release()
875 875
876 876 if nolfiles and nonormalfiles:
877 877 raise error.Abort(_(b'no files to copy'))
878 878
879 879 return result
880 880
881 881
882 882 # When the user calls revert, we have to be careful to not revert any
883 883 # changes to other largefiles accidentally. This means we have to keep
884 884 # track of the largefiles that are being reverted so we only pull down
885 885 # the necessary largefiles.
886 886 #
887 887 # Standins are only updated (to match the hash of largefiles) before
888 888 # commits. Update the standins then run the original revert, changing
889 889 # the matcher to hit standins instead of largefiles. Based on the
890 890 # resulting standins update the largefiles.
891 891 @eh.wrapfunction(cmdutil, b'revert')
892 892 def overriderevert(orig, ui, repo, ctx, *pats, **opts):
893 893 # Because we put the standins in a bad state (by updating them)
894 894 # and then return them to a correct state we need to lock to
895 895 # prevent others from changing them in their incorrect state.
896 896 with repo.wlock():
897 897 lfdirstate = lfutil.openlfdirstate(ui, repo)
898 898 s = lfutil.lfdirstatestatus(lfdirstate, repo)
899 899 lfdirstate.write()
900 900 for lfile in s.modified:
901 901 lfutil.updatestandin(repo, lfile, lfutil.standin(lfile))
902 902 for lfile in s.deleted:
903 903 fstandin = lfutil.standin(lfile)
904 904 if repo.wvfs.exists(fstandin):
905 905 repo.wvfs.unlink(fstandin)
906 906
907 907 oldstandins = lfutil.getstandinsstate(repo)
908 908
909 909 def overridematch(
910 910 orig,
911 911 mctx,
912 912 pats=(),
913 913 opts=None,
914 914 globbed=False,
915 915 default=b'relpath',
916 916 badfn=None,
917 917 ):
918 918 if opts is None:
919 919 opts = {}
920 920 match = orig(mctx, pats, opts, globbed, default, badfn=badfn)
921 921 m = copy.copy(match)
922 922
923 923 # revert supports recursing into subrepos, and though largefiles
924 924 # currently doesn't work correctly in that case, this match is
925 925 # called, so the lfdirstate above may not be the correct one for
926 926 # this invocation of match.
927 927 lfdirstate = lfutil.openlfdirstate(
928 928 mctx.repo().ui, mctx.repo(), False
929 929 )
930 930
931 931 wctx = repo[None]
932 932 matchfiles = []
933 933 for f in m._files:
934 934 standin = lfutil.standin(f)
935 935 if standin in ctx or standin in mctx:
936 936 matchfiles.append(standin)
937 937 elif standin in wctx or lfdirstate[f] == b'r':
938 938 continue
939 939 else:
940 940 matchfiles.append(f)
941 941 m._files = matchfiles
942 942 m._fileset = set(m._files)
943 943 origmatchfn = m.matchfn
944 944
945 945 def matchfn(f):
946 946 lfile = lfutil.splitstandin(f)
947 947 if lfile is not None:
948 948 return origmatchfn(lfile) and (f in ctx or f in mctx)
949 949 return origmatchfn(f)
950 950
951 951 m.matchfn = matchfn
952 952 return m
953 953
954 954 with extensions.wrappedfunction(scmutil, b'match', overridematch):
955 955 orig(ui, repo, ctx, *pats, **opts)
956 956
957 957 newstandins = lfutil.getstandinsstate(repo)
958 958 filelist = lfutil.getlfilestoupdate(oldstandins, newstandins)
959 959 # lfdirstate should be 'normallookup'-ed for updated files,
960 960 # because reverting doesn't touch dirstate for 'normal' files
961 961 # when target revision is explicitly specified: in such case,
962 962 # 'n' and valid timestamp in dirstate doesn't ensure 'clean'
963 963 # of target (standin) file.
964 964 lfcommands.updatelfiles(
965 965 ui, repo, filelist, printmessage=False, normallookup=True
966 966 )
967 967
968 968
969 969 # after pulling changesets, we need to take some extra care to get
970 970 # largefiles updated remotely
971 971 @eh.wrapcommand(
972 972 b'pull',
973 973 opts=[
974 974 (
975 975 b'',
976 976 b'all-largefiles',
977 977 None,
978 978 _(b'download all pulled versions of largefiles (DEPRECATED)'),
979 979 ),
980 980 (
981 981 b'',
982 982 b'lfrev',
983 983 [],
984 984 _(b'download largefiles for these revisions'),
985 985 _(b'REV'),
986 986 ),
987 987 ],
988 988 )
989 989 def overridepull(orig, ui, repo, source=None, **opts):
990 990 revsprepull = len(repo)
991 991 if not source:
992 992 source = b'default'
993 993 repo.lfpullsource = source
994 994 result = orig(ui, repo, source, **opts)
995 995 revspostpull = len(repo)
996 996 lfrevs = opts.get('lfrev', [])
997 997 if opts.get('all_largefiles'):
998 998 lfrevs.append(b'pulled()')
999 999 if lfrevs and revspostpull > revsprepull:
1000 1000 numcached = 0
1001 1001 repo.firstpulled = revsprepull # for pulled() revset expression
1002 1002 try:
1003 1003 for rev in scmutil.revrange(repo, lfrevs):
1004 1004 ui.note(_(b'pulling largefiles for revision %d\n') % rev)
1005 1005 (cached, missing) = lfcommands.cachelfiles(ui, repo, rev)
1006 1006 numcached += len(cached)
1007 1007 finally:
1008 1008 del repo.firstpulled
1009 1009 ui.status(_(b"%d largefiles cached\n") % numcached)
1010 1010 return result
1011 1011
1012 1012
1013 1013 @eh.wrapcommand(
1014 1014 b'push',
1015 1015 opts=[
1016 1016 (
1017 1017 b'',
1018 1018 b'lfrev',
1019 1019 [],
1020 1020 _(b'upload largefiles for these revisions'),
1021 1021 _(b'REV'),
1022 1022 )
1023 1023 ],
1024 1024 )
1025 1025 def overridepush(orig, ui, repo, *args, **kwargs):
1026 1026 """Override push command and store --lfrev parameters in opargs"""
1027 1027 lfrevs = kwargs.pop('lfrev', None)
1028 1028 if lfrevs:
1029 1029 opargs = kwargs.setdefault('opargs', {})
1030 1030 opargs[b'lfrevs'] = scmutil.revrange(repo, lfrevs)
1031 1031 return orig(ui, repo, *args, **kwargs)
1032 1032
1033 1033
1034 1034 @eh.wrapfunction(exchange, b'pushoperation')
1035 1035 def exchangepushoperation(orig, *args, **kwargs):
1036 1036 """Override pushoperation constructor and store lfrevs parameter"""
1037 1037 lfrevs = kwargs.pop('lfrevs', None)
1038 1038 pushop = orig(*args, **kwargs)
1039 1039 pushop.lfrevs = lfrevs
1040 1040 return pushop
1041 1041
1042 1042
1043 1043 @eh.revsetpredicate(b'pulled()')
1044 1044 def pulledrevsetsymbol(repo, subset, x):
1045 1045 """Changesets that just has been pulled.
1046 1046
1047 1047 Only available with largefiles from pull --lfrev expressions.
1048 1048
1049 1049 .. container:: verbose
1050 1050
1051 1051 Some examples:
1052 1052
1053 1053 - pull largefiles for all new changesets::
1054 1054
1055 1055 hg pull -lfrev "pulled()"
1056 1056
1057 1057 - pull largefiles for all new branch heads::
1058 1058
1059 1059 hg pull -lfrev "head(pulled()) and not closed()"
1060 1060
1061 1061 """
1062 1062
1063 1063 try:
1064 1064 firstpulled = repo.firstpulled
1065 1065 except AttributeError:
1066 1066 raise error.Abort(_(b"pulled() only available in --lfrev"))
1067 1067 return smartset.baseset([r for r in subset if r >= firstpulled])
1068 1068
1069 1069
1070 1070 @eh.wrapcommand(
1071 1071 b'clone',
1072 1072 opts=[
1073 1073 (
1074 1074 b'',
1075 1075 b'all-largefiles',
1076 1076 None,
1077 1077 _(b'download all versions of all largefiles'),
1078 1078 )
1079 1079 ],
1080 1080 )
1081 1081 def overrideclone(orig, ui, source, dest=None, **opts):
1082 1082 d = dest
1083 1083 if d is None:
1084 1084 d = hg.defaultdest(source)
1085 1085 if opts.get('all_largefiles') and not hg.islocal(d):
1086 1086 raise error.Abort(
1087 1087 _(b'--all-largefiles is incompatible with non-local destination %s')
1088 1088 % d
1089 1089 )
1090 1090
1091 1091 return orig(ui, source, dest, **opts)
1092 1092
1093 1093
1094 1094 @eh.wrapfunction(hg, b'clone')
1095 1095 def hgclone(orig, ui, opts, *args, **kwargs):
1096 1096 result = orig(ui, opts, *args, **kwargs)
1097 1097
1098 1098 if result is not None:
1099 1099 sourcerepo, destrepo = result
1100 1100 repo = destrepo.local()
1101 1101
1102 1102 # When cloning to a remote repo (like through SSH), no repo is available
1103 1103 # from the peer. Therefore the largefiles can't be downloaded and the
1104 1104 # hgrc can't be updated.
1105 1105 if not repo:
1106 1106 return result
1107 1107
1108 1108 # Caching is implicitly limited to 'rev' option, since the dest repo was
1109 1109 # truncated at that point. The user may expect a download count with
1110 1110 # this option, so attempt whether or not this is a largefile repo.
1111 1111 if opts.get(b'all_largefiles'):
1112 1112 success, missing = lfcommands.downloadlfiles(ui, repo)
1113 1113
1114 1114 if missing != 0:
1115 1115 return None
1116 1116
1117 1117 return result
1118 1118
1119 1119
1120 1120 @eh.wrapcommand(b'rebase', extension=b'rebase')
1121 1121 def overriderebasecmd(orig, ui, repo, **opts):
1122 1122 if not util.safehasattr(repo, b'_largefilesenabled'):
1123 1123 return orig(ui, repo, **opts)
1124 1124
1125 1125 resuming = opts.get('continue')
1126 1126 repo._lfcommithooks.append(lfutil.automatedcommithook(resuming))
1127 1127 repo._lfstatuswriters.append(lambda *msg, **opts: None)
1128 1128 try:
1129 1129 with ui.configoverride(
1130 1130 {(b'rebase', b'experimental.inmemory'): False}, b"largefiles"
1131 1131 ):
1132 1132 return orig(ui, repo, **opts)
1133 1133 finally:
1134 1134 repo._lfstatuswriters.pop()
1135 1135 repo._lfcommithooks.pop()
1136 1136
1137 1137
1138 1138 @eh.extsetup
1139 1139 def overriderebase(ui):
1140 1140 try:
1141 1141 rebase = extensions.find(b'rebase')
1142 1142 except KeyError:
1143 1143 pass
1144 1144 else:
1145 1145
1146 1146 def _dorebase(orig, *args, **kwargs):
1147 1147 kwargs['inmemory'] = False
1148 1148 return orig(*args, **kwargs)
1149 1149
1150 1150 extensions.wrapfunction(rebase, b'_dorebase', _dorebase)
1151 1151
1152 1152
1153 1153 @eh.wrapcommand(b'archive')
1154 1154 def overridearchivecmd(orig, ui, repo, dest, **opts):
1155 1155 with lfstatus(repo.unfiltered()):
1156 1156 return orig(ui, repo.unfiltered(), dest, **opts)
1157 1157
1158 1158
1159 1159 @eh.wrapfunction(webcommands, b'archive')
1160 1160 def hgwebarchive(orig, web):
1161 1161 with lfstatus(web.repo):
1162 1162 return orig(web)
1163 1163
1164 1164
1165 1165 @eh.wrapfunction(archival, b'archive')
1166 1166 def overridearchive(
1167 1167 orig,
1168 1168 repo,
1169 1169 dest,
1170 1170 node,
1171 1171 kind,
1172 1172 decode=True,
1173 1173 match=None,
1174 1174 prefix=b'',
1175 1175 mtime=None,
1176 1176 subrepos=None,
1177 1177 ):
1178 1178 # For some reason setting repo.lfstatus in hgwebarchive only changes the
1179 1179 # unfiltered repo's attr, so check that as well.
1180 1180 if not repo.lfstatus and not repo.unfiltered().lfstatus:
1181 1181 return orig(
1182 1182 repo, dest, node, kind, decode, match, prefix, mtime, subrepos
1183 1183 )
1184 1184
1185 1185 # No need to lock because we are only reading history and
1186 1186 # largefile caches, neither of which are modified.
1187 1187 if node is not None:
1188 1188 lfcommands.cachelfiles(repo.ui, repo, node)
1189 1189
1190 1190 if kind not in archival.archivers:
1191 1191 raise error.Abort(_(b"unknown archive type '%s'") % kind)
1192 1192
1193 1193 ctx = repo[node]
1194 1194
1195 1195 if kind == b'files':
1196 1196 if prefix:
1197 1197 raise error.Abort(_(b'cannot give prefix when archiving to files'))
1198 1198 else:
1199 1199 prefix = archival.tidyprefix(dest, kind, prefix)
1200 1200
1201 1201 def write(name, mode, islink, getdata):
1202 1202 if match and not match(name):
1203 1203 return
1204 1204 data = getdata()
1205 1205 if decode:
1206 1206 data = repo.wwritedata(name, data)
1207 1207 archiver.addfile(prefix + name, mode, islink, data)
1208 1208
1209 1209 archiver = archival.archivers[kind](dest, mtime or ctx.date()[0])
1210 1210
1211 1211 if repo.ui.configbool(b"ui", b"archivemeta"):
1212 1212 write(
1213 1213 b'.hg_archival.txt',
1214 1214 0o644,
1215 1215 False,
1216 1216 lambda: archival.buildmetadata(ctx),
1217 1217 )
1218 1218
1219 1219 for f in ctx:
1220 1220 ff = ctx.flags(f)
1221 1221 getdata = ctx[f].data
1222 1222 lfile = lfutil.splitstandin(f)
1223 1223 if lfile is not None:
1224 1224 if node is not None:
1225 1225 path = lfutil.findfile(repo, getdata().strip())
1226 1226
1227 1227 if path is None:
1228 1228 raise error.Abort(
1229 1229 _(
1230 1230 b'largefile %s not found in repo store or system cache'
1231 1231 )
1232 1232 % lfile
1233 1233 )
1234 1234 else:
1235 1235 path = lfile
1236 1236
1237 1237 f = lfile
1238 1238
1239 1239 getdata = lambda: util.readfile(path)
1240 1240 write(f, b'x' in ff and 0o755 or 0o644, b'l' in ff, getdata)
1241 1241
1242 1242 if subrepos:
1243 1243 for subpath in sorted(ctx.substate):
1244 1244 sub = ctx.workingsub(subpath)
1245 1245 submatch = matchmod.subdirmatcher(subpath, match)
1246 1246 subprefix = prefix + subpath + b'/'
1247 1247
1248 1248 # TODO: Only hgsubrepo instances have `_repo`, so figure out how to
1249 1249 # infer and possibly set lfstatus in hgsubrepoarchive. That would
1250 1250 # allow only hgsubrepos to set this, instead of the current scheme
1251 1251 # where the parent sets this for the child.
1252 1252 with (
1253 1253 util.safehasattr(sub, '_repo')
1254 1254 and lfstatus(sub._repo)
1255 1255 or util.nullcontextmanager()
1256 1256 ):
1257 1257 sub.archive(archiver, subprefix, submatch)
1258 1258
1259 1259 archiver.done()
1260 1260
1261 1261
1262 1262 @eh.wrapfunction(subrepo.hgsubrepo, b'archive')
1263 1263 def hgsubrepoarchive(orig, repo, archiver, prefix, match=None, decode=True):
1264 1264 lfenabled = util.safehasattr(repo._repo, b'_largefilesenabled')
1265 1265 if not lfenabled or not repo._repo.lfstatus:
1266 1266 return orig(repo, archiver, prefix, match, decode)
1267 1267
1268 1268 repo._get(repo._state + (b'hg',))
1269 1269 rev = repo._state[1]
1270 1270 ctx = repo._repo[rev]
1271 1271
1272 1272 if ctx.node() is not None:
1273 1273 lfcommands.cachelfiles(repo.ui, repo._repo, ctx.node())
1274 1274
1275 1275 def write(name, mode, islink, getdata):
1276 1276 # At this point, the standin has been replaced with the largefile name,
1277 1277 # so the normal matcher works here without the lfutil variants.
1278 1278 if match and not match(f):
1279 1279 return
1280 1280 data = getdata()
1281 1281 if decode:
1282 1282 data = repo._repo.wwritedata(name, data)
1283 1283
1284 1284 archiver.addfile(prefix + name, mode, islink, data)
1285 1285
1286 1286 for f in ctx:
1287 1287 ff = ctx.flags(f)
1288 1288 getdata = ctx[f].data
1289 1289 lfile = lfutil.splitstandin(f)
1290 1290 if lfile is not None:
1291 1291 if ctx.node() is not None:
1292 1292 path = lfutil.findfile(repo._repo, getdata().strip())
1293 1293
1294 1294 if path is None:
1295 1295 raise error.Abort(
1296 1296 _(
1297 1297 b'largefile %s not found in repo store or system cache'
1298 1298 )
1299 1299 % lfile
1300 1300 )
1301 1301 else:
1302 1302 path = lfile
1303 1303
1304 1304 f = lfile
1305 1305
1306 1306 getdata = lambda: util.readfile(os.path.join(prefix, path))
1307 1307
1308 1308 write(f, b'x' in ff and 0o755 or 0o644, b'l' in ff, getdata)
1309 1309
1310 1310 for subpath in sorted(ctx.substate):
1311 1311 sub = ctx.workingsub(subpath)
1312 1312 submatch = matchmod.subdirmatcher(subpath, match)
1313 1313 subprefix = prefix + subpath + b'/'
1314 1314 # TODO: Only hgsubrepo instances have `_repo`, so figure out how to
1315 1315 # infer and possibly set lfstatus at the top of this function. That
1316 1316 # would allow only hgsubrepos to set this, instead of the current scheme
1317 1317 # where the parent sets this for the child.
1318 1318 with (
1319 1319 util.safehasattr(sub, '_repo')
1320 1320 and lfstatus(sub._repo)
1321 1321 or util.nullcontextmanager()
1322 1322 ):
1323 1323 sub.archive(archiver, subprefix, submatch, decode)
1324 1324
1325 1325
1326 1326 # If a largefile is modified, the change is not reflected in its
1327 1327 # standin until a commit. cmdutil.bailifchanged() raises an exception
1328 1328 # if the repo has uncommitted changes. Wrap it to also check if
1329 1329 # largefiles were changed. This is used by bisect, backout and fetch.
1330 1330 @eh.wrapfunction(cmdutil, b'bailifchanged')
1331 1331 def overridebailifchanged(orig, repo, *args, **kwargs):
1332 1332 orig(repo, *args, **kwargs)
1333 1333 with lfstatus(repo):
1334 1334 s = repo.status()
1335 1335 if s.modified or s.added or s.removed or s.deleted:
1336 1336 raise error.Abort(_(b'uncommitted changes'))
1337 1337
1338 1338
1339 1339 @eh.wrapfunction(cmdutil, b'postcommitstatus')
1340 1340 def postcommitstatus(orig, repo, *args, **kwargs):
1341 1341 with lfstatus(repo):
1342 1342 return orig(repo, *args, **kwargs)
1343 1343
1344 1344
1345 1345 @eh.wrapfunction(cmdutil, b'forget')
1346 1346 def cmdutilforget(
1347 1347 orig, ui, repo, match, prefix, uipathfn, explicitonly, dryrun, interactive
1348 1348 ):
1349 1349 normalmatcher = composenormalfilematcher(match, repo[None].manifest())
1350 1350 bad, forgot = orig(
1351 1351 ui,
1352 1352 repo,
1353 1353 normalmatcher,
1354 1354 prefix,
1355 1355 uipathfn,
1356 1356 explicitonly,
1357 1357 dryrun,
1358 1358 interactive,
1359 1359 )
1360 1360 m = composelargefilematcher(match, repo[None].manifest())
1361 1361
1362 1362 with lfstatus(repo):
1363 1363 s = repo.status(match=m, clean=True)
1364 1364 manifest = repo[None].manifest()
1365 1365 forget = sorted(s.modified + s.added + s.deleted + s.clean)
1366 1366 forget = [f for f in forget if lfutil.standin(f) in manifest]
1367 1367
1368 1368 for f in forget:
1369 1369 fstandin = lfutil.standin(f)
1370 1370 if fstandin not in repo.dirstate and not repo.wvfs.isdir(fstandin):
1371 1371 ui.warn(
1372 1372 _(b'not removing %s: file is already untracked\n') % uipathfn(f)
1373 1373 )
1374 1374 bad.append(f)
1375 1375
1376 1376 for f in forget:
1377 1377 if ui.verbose or not m.exact(f):
1378 1378 ui.status(_(b'removing %s\n') % uipathfn(f))
1379 1379
1380 1380 # Need to lock because standin files are deleted then removed from the
1381 1381 # repository and we could race in-between.
1382 1382 with repo.wlock():
1383 1383 lfdirstate = lfutil.openlfdirstate(ui, repo)
1384 1384 for f in forget:
1385 1385 if lfdirstate[f] == b'a':
1386 1386 lfdirstate.drop(f)
1387 1387 else:
1388 1388 lfdirstate.remove(f)
1389 1389 lfdirstate.write()
1390 1390 standins = [lfutil.standin(f) for f in forget]
1391 1391 for f in standins:
1392 1392 repo.wvfs.unlinkpath(f, ignoremissing=True)
1393 1393 rejected = repo[None].forget(standins)
1394 1394
1395 1395 bad.extend(f for f in rejected if f in m.files())
1396 1396 forgot.extend(f for f in forget if f not in rejected)
1397 1397 return bad, forgot
1398 1398
1399 1399
1400 1400 def _getoutgoings(repo, other, missing, addfunc):
1401 1401 """get pairs of filename and largefile hash in outgoing revisions
1402 1402 in 'missing'.
1403 1403
1404 1404 largefiles already existing on 'other' repository are ignored.
1405 1405
1406 1406 'addfunc' is invoked with each unique pairs of filename and
1407 1407 largefile hash value.
1408 1408 """
1409 1409 knowns = set()
1410 1410 lfhashes = set()
1411 1411
1412 1412 def dedup(fn, lfhash):
1413 1413 k = (fn, lfhash)
1414 1414 if k not in knowns:
1415 1415 knowns.add(k)
1416 1416 lfhashes.add(lfhash)
1417 1417
1418 1418 lfutil.getlfilestoupload(repo, missing, dedup)
1419 1419 if lfhashes:
1420 1420 lfexists = storefactory.openstore(repo, other).exists(lfhashes)
1421 1421 for fn, lfhash in knowns:
1422 1422 if not lfexists[lfhash]: # lfhash doesn't exist on "other"
1423 1423 addfunc(fn, lfhash)
1424 1424
1425 1425
1426 1426 def outgoinghook(ui, repo, other, opts, missing):
1427 1427 if opts.pop(b'large', None):
1428 1428 lfhashes = set()
1429 1429 if ui.debugflag:
1430 1430 toupload = {}
1431 1431
1432 1432 def addfunc(fn, lfhash):
1433 1433 if fn not in toupload:
1434 1434 toupload[fn] = []
1435 1435 toupload[fn].append(lfhash)
1436 1436 lfhashes.add(lfhash)
1437 1437
1438 1438 def showhashes(fn):
1439 1439 for lfhash in sorted(toupload[fn]):
1440 1440 ui.debug(b' %s\n' % lfhash)
1441 1441
1442 1442 else:
1443 1443 toupload = set()
1444 1444
1445 1445 def addfunc(fn, lfhash):
1446 1446 toupload.add(fn)
1447 1447 lfhashes.add(lfhash)
1448 1448
1449 1449 def showhashes(fn):
1450 1450 pass
1451 1451
1452 1452 _getoutgoings(repo, other, missing, addfunc)
1453 1453
1454 1454 if not toupload:
1455 1455 ui.status(_(b'largefiles: no files to upload\n'))
1456 1456 else:
1457 1457 ui.status(
1458 1458 _(b'largefiles to upload (%d entities):\n') % (len(lfhashes))
1459 1459 )
1460 1460 for file in sorted(toupload):
1461 1461 ui.status(lfutil.splitstandin(file) + b'\n')
1462 1462 showhashes(file)
1463 1463 ui.status(b'\n')
1464 1464
1465 1465
1466 1466 @eh.wrapcommand(
1467 1467 b'outgoing', opts=[(b'', b'large', None, _(b'display outgoing largefiles'))]
1468 1468 )
1469 1469 def _outgoingcmd(orig, *args, **kwargs):
1470 1470 # Nothing to do here other than add the extra help option- the hook above
1471 1471 # processes it.
1472 1472 return orig(*args, **kwargs)
1473 1473
1474 1474
1475 1475 def summaryremotehook(ui, repo, opts, changes):
1476 1476 largeopt = opts.get(b'large', False)
1477 1477 if changes is None:
1478 1478 if largeopt:
1479 1479 return (False, True) # only outgoing check is needed
1480 1480 else:
1481 1481 return (False, False)
1482 1482 elif largeopt:
1483 1483 url, branch, peer, outgoing = changes[1]
1484 1484 if peer is None:
1485 1485 # i18n: column positioning for "hg summary"
1486 1486 ui.status(_(b'largefiles: (no remote repo)\n'))
1487 1487 return
1488 1488
1489 1489 toupload = set()
1490 1490 lfhashes = set()
1491 1491
1492 1492 def addfunc(fn, lfhash):
1493 1493 toupload.add(fn)
1494 1494 lfhashes.add(lfhash)
1495 1495
1496 1496 _getoutgoings(repo, peer, outgoing.missing, addfunc)
1497 1497
1498 1498 if not toupload:
1499 1499 # i18n: column positioning for "hg summary"
1500 1500 ui.status(_(b'largefiles: (no files to upload)\n'))
1501 1501 else:
1502 1502 # i18n: column positioning for "hg summary"
1503 1503 ui.status(
1504 1504 _(b'largefiles: %d entities for %d files to upload\n')
1505 1505 % (len(lfhashes), len(toupload))
1506 1506 )
1507 1507
1508 1508
1509 1509 @eh.wrapcommand(
1510 1510 b'summary', opts=[(b'', b'large', None, _(b'display outgoing largefiles'))]
1511 1511 )
1512 1512 def overridesummary(orig, ui, repo, *pats, **opts):
1513 1513 with lfstatus(repo):
1514 1514 orig(ui, repo, *pats, **opts)
1515 1515
1516 1516
1517 1517 @eh.wrapfunction(scmutil, b'addremove')
1518 1518 def scmutiladdremove(orig, repo, matcher, prefix, uipathfn, opts=None):
1519 1519 if opts is None:
1520 1520 opts = {}
1521 1521 if not lfutil.islfilesrepo(repo):
1522 1522 return orig(repo, matcher, prefix, uipathfn, opts)
1523 1523 # Get the list of missing largefiles so we can remove them
1524 1524 lfdirstate = lfutil.openlfdirstate(repo.ui, repo)
1525 1525 unsure, s = lfdirstate.status(
1526 1526 matchmod.always(),
1527 1527 subrepos=[],
1528 1528 ignored=False,
1529 1529 clean=False,
1530 1530 unknown=False,
1531 1531 )
1532 1532
1533 1533 # Call into the normal remove code, but the removing of the standin, we want
1534 1534 # to have handled by original addremove. Monkey patching here makes sure
1535 1535 # we don't remove the standin in the largefiles code, preventing a very
1536 1536 # confused state later.
1537 1537 if s.deleted:
1538 1538 m = copy.copy(matcher)
1539 1539
1540 1540 # The m._files and m._map attributes are not changed to the deleted list
1541 1541 # because that affects the m.exact() test, which in turn governs whether
1542 1542 # or not the file name is printed, and how. Simply limit the original
1543 1543 # matches to those in the deleted status list.
1544 1544 matchfn = m.matchfn
1545 1545 m.matchfn = lambda f: f in s.deleted and matchfn(f)
1546 1546
1547 1547 removelargefiles(
1548 1548 repo.ui,
1549 1549 repo,
1550 1550 True,
1551 1551 m,
1552 1552 uipathfn,
1553 1553 opts.get(b'dry_run'),
1554 1554 **pycompat.strkwargs(opts)
1555 1555 )
1556 1556 # Call into the normal add code, and any files that *should* be added as
1557 1557 # largefiles will be
1558 1558 added, bad = addlargefiles(
1559 1559 repo.ui, repo, True, matcher, uipathfn, **pycompat.strkwargs(opts)
1560 1560 )
1561 1561 # Now that we've handled largefiles, hand off to the original addremove
1562 1562 # function to take care of the rest. Make sure it doesn't do anything with
1563 1563 # largefiles by passing a matcher that will ignore them.
1564 1564 matcher = composenormalfilematcher(matcher, repo[None].manifest(), added)
1565 1565 return orig(repo, matcher, prefix, uipathfn, opts)
1566 1566
1567 1567
1568 1568 # Calling purge with --all will cause the largefiles to be deleted.
1569 1569 # Override repo.status to prevent this from happening.
1570 1570 @eh.wrapcommand(b'purge')
1571 1571 def overridepurge(orig, ui, repo, *dirs, **opts):
1572 1572 # XXX Monkey patching a repoview will not work. The assigned attribute will
1573 1573 # be set on the unfiltered repo, but we will only lookup attributes in the
1574 1574 # unfiltered repo if the lookup in the repoview object itself fails. As the
1575 1575 # monkey patched method exists on the repoview class the lookup will not
1576 1576 # fail. As a result, the original version will shadow the monkey patched
1577 1577 # one, defeating the monkey patch.
1578 1578 #
1579 1579 # As a work around we use an unfiltered repo here. We should do something
1580 1580 # cleaner instead.
1581 1581 repo = repo.unfiltered()
1582 1582 oldstatus = repo.status
1583 1583
1584 1584 def overridestatus(
1585 1585 node1=b'.',
1586 1586 node2=None,
1587 1587 match=None,
1588 1588 ignored=False,
1589 1589 clean=False,
1590 1590 unknown=False,
1591 1591 listsubrepos=False,
1592 1592 ):
1593 1593 r = oldstatus(
1594 1594 node1, node2, match, ignored, clean, unknown, listsubrepos
1595 1595 )
1596 1596 lfdirstate = lfutil.openlfdirstate(ui, repo)
1597 1597 unknown = [f for f in r.unknown if lfdirstate[f] == b'?']
1598 1598 ignored = [f for f in r.ignored if lfdirstate[f] == b'?']
1599 1599 return scmutil.status(
1600 1600 r.modified, r.added, r.removed, r.deleted, unknown, ignored, r.clean
1601 1601 )
1602 1602
1603 1603 repo.status = overridestatus
1604 1604 orig(ui, repo, *dirs, **opts)
1605 1605 repo.status = oldstatus
1606 1606
1607 1607
1608 1608 @eh.wrapcommand(b'rollback')
1609 1609 def overriderollback(orig, ui, repo, **opts):
1610 1610 with repo.wlock():
1611 1611 before = repo.dirstate.parents()
1612 1612 orphans = {
1613 1613 f
1614 1614 for f in repo.dirstate
1615 1615 if lfutil.isstandin(f) and repo.dirstate[f] != b'r'
1616 1616 }
1617 1617 result = orig(ui, repo, **opts)
1618 1618 after = repo.dirstate.parents()
1619 1619 if before == after:
1620 1620 return result # no need to restore standins
1621 1621
1622 1622 pctx = repo[b'.']
1623 1623 for f in repo.dirstate:
1624 1624 if lfutil.isstandin(f):
1625 1625 orphans.discard(f)
1626 1626 if repo.dirstate[f] == b'r':
1627 1627 repo.wvfs.unlinkpath(f, ignoremissing=True)
1628 1628 elif f in pctx:
1629 1629 fctx = pctx[f]
1630 1630 repo.wwrite(f, fctx.data(), fctx.flags())
1631 1631 else:
1632 1632 # content of standin is not so important in 'a',
1633 1633 # 'm' or 'n' (coming from the 2nd parent) cases
1634 1634 lfutil.writestandin(repo, f, b'', False)
1635 1635 for standin in orphans:
1636 1636 repo.wvfs.unlinkpath(standin, ignoremissing=True)
1637 1637
1638 1638 lfdirstate = lfutil.openlfdirstate(ui, repo)
1639 1639 orphans = set(lfdirstate)
1640 1640 lfiles = lfutil.listlfiles(repo)
1641 1641 for file in lfiles:
1642 1642 lfutil.synclfdirstate(repo, lfdirstate, file, True)
1643 1643 orphans.discard(file)
1644 1644 for lfile in orphans:
1645 1645 lfdirstate.drop(lfile)
1646 1646 lfdirstate.write()
1647 1647 return result
1648 1648
1649 1649
1650 1650 @eh.wrapcommand(b'transplant', extension=b'transplant')
1651 1651 def overridetransplant(orig, ui, repo, *revs, **opts):
1652 1652 resuming = opts.get('continue')
1653 1653 repo._lfcommithooks.append(lfutil.automatedcommithook(resuming))
1654 1654 repo._lfstatuswriters.append(lambda *msg, **opts: None)
1655 1655 try:
1656 1656 result = orig(ui, repo, *revs, **opts)
1657 1657 finally:
1658 1658 repo._lfstatuswriters.pop()
1659 1659 repo._lfcommithooks.pop()
1660 1660 return result
1661 1661
1662 1662
1663 1663 @eh.wrapcommand(b'cat')
1664 1664 def overridecat(orig, ui, repo, file1, *pats, **opts):
1665 1665 opts = pycompat.byteskwargs(opts)
1666 1666 ctx = scmutil.revsingle(repo, opts.get(b'rev'))
1667 1667 err = 1
1668 1668 notbad = set()
1669 1669 m = scmutil.match(ctx, (file1,) + pats, opts)
1670 1670 origmatchfn = m.matchfn
1671 1671
1672 1672 def lfmatchfn(f):
1673 1673 if origmatchfn(f):
1674 1674 return True
1675 1675 lf = lfutil.splitstandin(f)
1676 1676 if lf is None:
1677 1677 return False
1678 1678 notbad.add(lf)
1679 1679 return origmatchfn(lf)
1680 1680
1681 1681 m.matchfn = lfmatchfn
1682 1682 origbadfn = m.bad
1683 1683
1684 1684 def lfbadfn(f, msg):
1685 1685 if not f in notbad:
1686 1686 origbadfn(f, msg)
1687 1687
1688 1688 m.bad = lfbadfn
1689 1689
1690 1690 origvisitdirfn = m.visitdir
1691 1691
1692 1692 def lfvisitdirfn(dir):
1693 1693 if dir == lfutil.shortname:
1694 1694 return True
1695 1695 ret = origvisitdirfn(dir)
1696 1696 if ret:
1697 1697 return ret
1698 1698 lf = lfutil.splitstandin(dir)
1699 1699 if lf is None:
1700 1700 return False
1701 1701 return origvisitdirfn(lf)
1702 1702
1703 1703 m.visitdir = lfvisitdirfn
1704 1704
1705 1705 for f in ctx.walk(m):
1706 1706 with cmdutil.makefileobj(ctx, opts.get(b'output'), pathname=f) as fp:
1707 1707 lf = lfutil.splitstandin(f)
1708 1708 if lf is None or origmatchfn(f):
1709 1709 # duplicating unreachable code from commands.cat
1710 1710 data = ctx[f].data()
1711 1711 if opts.get(b'decode'):
1712 1712 data = repo.wwritedata(f, data)
1713 1713 fp.write(data)
1714 1714 else:
1715 1715 hash = lfutil.readasstandin(ctx[f])
1716 1716 if not lfutil.inusercache(repo.ui, hash):
1717 1717 store = storefactory.openstore(repo)
1718 1718 success, missing = store.get([(lf, hash)])
1719 1719 if len(success) != 1:
1720 1720 raise error.Abort(
1721 1721 _(
1722 1722 b'largefile %s is not in cache and could not be '
1723 1723 b'downloaded'
1724 1724 )
1725 1725 % lf
1726 1726 )
1727 1727 path = lfutil.usercachepath(repo.ui, hash)
1728 1728 with open(path, b"rb") as fpin:
1729 1729 for chunk in util.filechunkiter(fpin):
1730 1730 fp.write(chunk)
1731 1731 err = 0
1732 1732 return err
1733 1733
1734 1734
1735 1735 @eh.wrapfunction(merge, b'_update')
1736 1736 def mergeupdate(orig, repo, node, branchmerge, force, *args, **kwargs):
1737 1737 matcher = kwargs.get('matcher', None)
1738 1738 # note if this is a partial update
1739 1739 partial = matcher and not matcher.always()
1740 1740 with repo.wlock():
1741 1741 # branch | | |
1742 1742 # merge | force | partial | action
1743 1743 # -------+-------+---------+--------------
1744 1744 # x | x | x | linear-merge
1745 1745 # o | x | x | branch-merge
1746 1746 # x | o | x | overwrite (as clean update)
1747 1747 # o | o | x | force-branch-merge (*1)
1748 1748 # x | x | o | (*)
1749 1749 # o | x | o | (*)
1750 1750 # x | o | o | overwrite (as revert)
1751 1751 # o | o | o | (*)
1752 1752 #
1753 1753 # (*) don't care
1754 1754 # (*1) deprecated, but used internally (e.g: "rebase --collapse")
1755 1755
1756 1756 lfdirstate = lfutil.openlfdirstate(repo.ui, repo)
1757 1757 unsure, s = lfdirstate.status(
1758 1758 matchmod.always(),
1759 1759 subrepos=[],
1760 1760 ignored=False,
1761 1761 clean=True,
1762 1762 unknown=False,
1763 1763 )
1764 1764 oldclean = set(s.clean)
1765 1765 pctx = repo[b'.']
1766 1766 dctx = repo[node]
1767 1767 for lfile in unsure + s.modified:
1768 1768 lfileabs = repo.wvfs.join(lfile)
1769 1769 if not repo.wvfs.exists(lfileabs):
1770 1770 continue
1771 1771 lfhash = lfutil.hashfile(lfileabs)
1772 1772 standin = lfutil.standin(lfile)
1773 1773 lfutil.writestandin(
1774 1774 repo, standin, lfhash, lfutil.getexecutable(lfileabs)
1775 1775 )
1776 1776 if standin in pctx and lfhash == lfutil.readasstandin(
1777 1777 pctx[standin]
1778 1778 ):
1779 1779 oldclean.add(lfile)
1780 1780 for lfile in s.added:
1781 1781 fstandin = lfutil.standin(lfile)
1782 1782 if fstandin not in dctx:
1783 1783 # in this case, content of standin file is meaningless
1784 1784 # (in dctx, lfile is unknown, or normal file)
1785 1785 continue
1786 1786 lfutil.updatestandin(repo, lfile, fstandin)
1787 1787 # mark all clean largefiles as dirty, just in case the update gets
1788 1788 # interrupted before largefiles and lfdirstate are synchronized
1789 1789 for lfile in oldclean:
1790 1790 lfdirstate.normallookup(lfile)
1791 1791 lfdirstate.write()
1792 1792
1793 1793 oldstandins = lfutil.getstandinsstate(repo)
1794 1794 wc = kwargs.get('wc')
1795 1795 if wc and wc.isinmemory():
1796 1796 # largefiles is not a good candidate for in-memory merge (large
1797 1797 # files, custom dirstate, matcher usage).
1798 1798 raise error.ProgrammingError(
1799 1799 b'largefiles is not compatible with in-memory merge'
1800 1800 )
1801 1801 result = orig(repo, node, branchmerge, force, *args, **kwargs)
1802 1802
1803 1803 newstandins = lfutil.getstandinsstate(repo)
1804 1804 filelist = lfutil.getlfilestoupdate(oldstandins, newstandins)
1805 1805
1806 1806 # to avoid leaving all largefiles as dirty and thus rehash them, mark
1807 1807 # all the ones that didn't change as clean
1808 1808 for lfile in oldclean.difference(filelist):
1809 1809 lfdirstate.normal(lfile)
1810 1810 lfdirstate.write()
1811 1811
1812 1812 if branchmerge or force or partial:
1813 1813 filelist.extend(s.deleted + s.removed)
1814 1814
1815 1815 lfcommands.updatelfiles(
1816 1816 repo.ui, repo, filelist=filelist, normallookup=partial
1817 1817 )
1818 1818
1819 1819 return result
1820 1820
1821 1821
1822 1822 @eh.wrapfunction(scmutil, b'marktouched')
1823 1823 def scmutilmarktouched(orig, repo, files, *args, **kwargs):
1824 1824 result = orig(repo, files, *args, **kwargs)
1825 1825
1826 1826 filelist = []
1827 1827 for f in files:
1828 1828 lf = lfutil.splitstandin(f)
1829 1829 if lf is not None:
1830 1830 filelist.append(lf)
1831 1831 if filelist:
1832 1832 lfcommands.updatelfiles(
1833 1833 repo.ui,
1834 1834 repo,
1835 1835 filelist=filelist,
1836 1836 printmessage=False,
1837 1837 normallookup=True,
1838 1838 )
1839 1839
1840 1840 return result
1841 1841
1842 1842
1843 1843 @eh.wrapfunction(upgrade_actions, b'preservedrequirements')
1844 1844 @eh.wrapfunction(upgrade_actions, b'supporteddestrequirements')
1845 1845 def upgraderequirements(orig, repo):
1846 1846 reqs = orig(repo)
1847 1847 if b'largefiles' in repo.requirements:
1848 1848 reqs.add(b'largefiles')
1849 1849 return reqs
1850 1850
1851 1851
1852 1852 _lfscheme = b'largefile://'
1853 1853
1854 1854
1855 1855 @eh.wrapfunction(urlmod, b'open')
1856 def openlargefile(orig, ui, url_, data=None):
1856 def openlargefile(orig, ui, url_, data=None, **kwargs):
1857 1857 if url_.startswith(_lfscheme):
1858 1858 if data:
1859 1859 msg = b"cannot use data on a 'largefile://' url"
1860 1860 raise error.ProgrammingError(msg)
1861 1861 lfid = url_[len(_lfscheme) :]
1862 1862 return storefactory.getlfile(ui, lfid)
1863 1863 else:
1864 return orig(ui, url_, data=data)
1864 return orig(ui, url_, data=data, **kwargs)
@@ -1,3924 +1,3924 b''
1 1 # cmdutil.py - help for command processing in mercurial
2 2 #
3 3 # Copyright 2005-2007 Matt Mackall <mpm@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 absolute_import
9 9
10 10 import copy as copymod
11 11 import errno
12 12 import os
13 13 import re
14 14
15 15 from .i18n import _
16 16 from .node import (
17 17 hex,
18 18 nullid,
19 19 short,
20 20 )
21 21 from .pycompat import (
22 22 getattr,
23 23 open,
24 24 setattr,
25 25 )
26 26 from .thirdparty import attr
27 27
28 28 from . import (
29 29 bookmarks,
30 30 changelog,
31 31 copies,
32 32 crecord as crecordmod,
33 33 dirstateguard,
34 34 encoding,
35 35 error,
36 36 formatter,
37 37 logcmdutil,
38 38 match as matchmod,
39 39 merge as mergemod,
40 40 mergestate as mergestatemod,
41 41 mergeutil,
42 42 obsolete,
43 43 patch,
44 44 pathutil,
45 45 phases,
46 46 pycompat,
47 47 repair,
48 48 revlog,
49 49 rewriteutil,
50 50 scmutil,
51 51 state as statemod,
52 52 subrepoutil,
53 53 templatekw,
54 54 templater,
55 55 util,
56 56 vfs as vfsmod,
57 57 )
58 58
59 59 from .utils import (
60 60 dateutil,
61 61 stringutil,
62 62 )
63 63
64 64 if pycompat.TYPE_CHECKING:
65 65 from typing import (
66 66 Any,
67 67 Dict,
68 68 )
69 69
70 70 for t in (Any, Dict):
71 71 assert t
72 72
73 73 stringio = util.stringio
74 74
75 75 # templates of common command options
76 76
77 77 dryrunopts = [
78 78 (b'n', b'dry-run', None, _(b'do not perform actions, just print output')),
79 79 ]
80 80
81 81 confirmopts = [
82 82 (b'', b'confirm', None, _(b'ask before applying actions')),
83 83 ]
84 84
85 85 remoteopts = [
86 86 (b'e', b'ssh', b'', _(b'specify ssh command to use'), _(b'CMD')),
87 87 (
88 88 b'',
89 89 b'remotecmd',
90 90 b'',
91 91 _(b'specify hg command to run on the remote side'),
92 92 _(b'CMD'),
93 93 ),
94 94 (
95 95 b'',
96 96 b'insecure',
97 97 None,
98 98 _(b'do not verify server certificate (ignoring web.cacerts config)'),
99 99 ),
100 100 ]
101 101
102 102 walkopts = [
103 103 (
104 104 b'I',
105 105 b'include',
106 106 [],
107 107 _(b'include names matching the given patterns'),
108 108 _(b'PATTERN'),
109 109 ),
110 110 (
111 111 b'X',
112 112 b'exclude',
113 113 [],
114 114 _(b'exclude names matching the given patterns'),
115 115 _(b'PATTERN'),
116 116 ),
117 117 ]
118 118
119 119 commitopts = [
120 120 (b'm', b'message', b'', _(b'use text as commit message'), _(b'TEXT')),
121 121 (b'l', b'logfile', b'', _(b'read commit message from file'), _(b'FILE')),
122 122 ]
123 123
124 124 commitopts2 = [
125 125 (
126 126 b'd',
127 127 b'date',
128 128 b'',
129 129 _(b'record the specified date as commit date'),
130 130 _(b'DATE'),
131 131 ),
132 132 (
133 133 b'u',
134 134 b'user',
135 135 b'',
136 136 _(b'record the specified user as committer'),
137 137 _(b'USER'),
138 138 ),
139 139 ]
140 140
141 141 commitopts3 = [
142 142 (b'D', b'currentdate', None, _(b'record the current date as commit date')),
143 143 (b'U', b'currentuser', None, _(b'record the current user as committer')),
144 144 ]
145 145
146 146 formatteropts = [
147 147 (b'T', b'template', b'', _(b'display with template'), _(b'TEMPLATE')),
148 148 ]
149 149
150 150 templateopts = [
151 151 (
152 152 b'',
153 153 b'style',
154 154 b'',
155 155 _(b'display using template map file (DEPRECATED)'),
156 156 _(b'STYLE'),
157 157 ),
158 158 (b'T', b'template', b'', _(b'display with template'), _(b'TEMPLATE')),
159 159 ]
160 160
161 161 logopts = [
162 162 (b'p', b'patch', None, _(b'show patch')),
163 163 (b'g', b'git', None, _(b'use git extended diff format')),
164 164 (b'l', b'limit', b'', _(b'limit number of changes displayed'), _(b'NUM')),
165 165 (b'M', b'no-merges', None, _(b'do not show merges')),
166 166 (b'', b'stat', None, _(b'output diffstat-style summary of changes')),
167 167 (b'G', b'graph', None, _(b"show the revision DAG")),
168 168 ] + templateopts
169 169
170 170 diffopts = [
171 171 (b'a', b'text', None, _(b'treat all files as text')),
172 172 (
173 173 b'g',
174 174 b'git',
175 175 None,
176 176 _(b'use git extended diff format (DEFAULT: diff.git)'),
177 177 ),
178 178 (b'', b'binary', None, _(b'generate binary diffs in git mode (default)')),
179 179 (b'', b'nodates', None, _(b'omit dates from diff headers')),
180 180 ]
181 181
182 182 diffwsopts = [
183 183 (
184 184 b'w',
185 185 b'ignore-all-space',
186 186 None,
187 187 _(b'ignore white space when comparing lines'),
188 188 ),
189 189 (
190 190 b'b',
191 191 b'ignore-space-change',
192 192 None,
193 193 _(b'ignore changes in the amount of white space'),
194 194 ),
195 195 (
196 196 b'B',
197 197 b'ignore-blank-lines',
198 198 None,
199 199 _(b'ignore changes whose lines are all blank'),
200 200 ),
201 201 (
202 202 b'Z',
203 203 b'ignore-space-at-eol',
204 204 None,
205 205 _(b'ignore changes in whitespace at EOL'),
206 206 ),
207 207 ]
208 208
209 209 diffopts2 = (
210 210 [
211 211 (b'', b'noprefix', None, _(b'omit a/ and b/ prefixes from filenames')),
212 212 (
213 213 b'p',
214 214 b'show-function',
215 215 None,
216 216 _(
217 217 b'show which function each change is in (DEFAULT: diff.showfunc)'
218 218 ),
219 219 ),
220 220 (b'', b'reverse', None, _(b'produce a diff that undoes the changes')),
221 221 ]
222 222 + diffwsopts
223 223 + [
224 224 (
225 225 b'U',
226 226 b'unified',
227 227 b'',
228 228 _(b'number of lines of context to show'),
229 229 _(b'NUM'),
230 230 ),
231 231 (b'', b'stat', None, _(b'output diffstat-style summary of changes')),
232 232 (
233 233 b'',
234 234 b'root',
235 235 b'',
236 236 _(b'produce diffs relative to subdirectory'),
237 237 _(b'DIR'),
238 238 ),
239 239 ]
240 240 )
241 241
242 242 mergetoolopts = [
243 243 (b't', b'tool', b'', _(b'specify merge tool'), _(b'TOOL')),
244 244 ]
245 245
246 246 similarityopts = [
247 247 (
248 248 b's',
249 249 b'similarity',
250 250 b'',
251 251 _(b'guess renamed files by similarity (0<=s<=100)'),
252 252 _(b'SIMILARITY'),
253 253 )
254 254 ]
255 255
256 256 subrepoopts = [(b'S', b'subrepos', None, _(b'recurse into subrepositories'))]
257 257
258 258 debugrevlogopts = [
259 259 (b'c', b'changelog', False, _(b'open changelog')),
260 260 (b'm', b'manifest', False, _(b'open manifest')),
261 261 (b'', b'dir', b'', _(b'open directory manifest')),
262 262 ]
263 263
264 264 # special string such that everything below this line will be ingored in the
265 265 # editor text
266 266 _linebelow = b"^HG: ------------------------ >8 ------------------------$"
267 267
268 268
269 269 def check_at_most_one_arg(opts, *args):
270 270 """abort if more than one of the arguments are in opts
271 271
272 272 Returns the unique argument or None if none of them were specified.
273 273 """
274 274
275 275 def to_display(name):
276 276 return pycompat.sysbytes(name).replace(b'_', b'-')
277 277
278 278 previous = None
279 279 for x in args:
280 280 if opts.get(x):
281 281 if previous:
282 282 raise error.InputError(
283 283 _(b'cannot specify both --%s and --%s')
284 284 % (to_display(previous), to_display(x))
285 285 )
286 286 previous = x
287 287 return previous
288 288
289 289
290 290 def check_incompatible_arguments(opts, first, others):
291 291 """abort if the first argument is given along with any of the others
292 292
293 293 Unlike check_at_most_one_arg(), `others` are not mutually exclusive
294 294 among themselves, and they're passed as a single collection.
295 295 """
296 296 for other in others:
297 297 check_at_most_one_arg(opts, first, other)
298 298
299 299
300 300 def resolvecommitoptions(ui, opts):
301 301 """modify commit options dict to handle related options
302 302
303 303 The return value indicates that ``rewrite.update-timestamp`` is the reason
304 304 the ``date`` option is set.
305 305 """
306 306 check_at_most_one_arg(opts, b'date', b'currentdate')
307 307 check_at_most_one_arg(opts, b'user', b'currentuser')
308 308
309 309 datemaydiffer = False # date-only change should be ignored?
310 310
311 311 if opts.get(b'currentdate'):
312 312 opts[b'date'] = b'%d %d' % dateutil.makedate()
313 313 elif (
314 314 not opts.get(b'date')
315 315 and ui.configbool(b'rewrite', b'update-timestamp')
316 316 and opts.get(b'currentdate') is None
317 317 ):
318 318 opts[b'date'] = b'%d %d' % dateutil.makedate()
319 319 datemaydiffer = True
320 320
321 321 if opts.get(b'currentuser'):
322 322 opts[b'user'] = ui.username()
323 323
324 324 return datemaydiffer
325 325
326 326
327 327 def checknotesize(ui, opts):
328 328 """ make sure note is of valid format """
329 329
330 330 note = opts.get(b'note')
331 331 if not note:
332 332 return
333 333
334 334 if len(note) > 255:
335 335 raise error.InputError(_(b"cannot store a note of more than 255 bytes"))
336 336 if b'\n' in note:
337 337 raise error.InputError(_(b"note cannot contain a newline"))
338 338
339 339
340 340 def ishunk(x):
341 341 hunkclasses = (crecordmod.uihunk, patch.recordhunk)
342 342 return isinstance(x, hunkclasses)
343 343
344 344
345 345 def newandmodified(chunks, originalchunks):
346 346 newlyaddedandmodifiedfiles = set()
347 347 alsorestore = set()
348 348 for chunk in chunks:
349 349 if (
350 350 ishunk(chunk)
351 351 and chunk.header.isnewfile()
352 352 and chunk not in originalchunks
353 353 ):
354 354 newlyaddedandmodifiedfiles.add(chunk.header.filename())
355 355 alsorestore.update(
356 356 set(chunk.header.files()) - {chunk.header.filename()}
357 357 )
358 358 return newlyaddedandmodifiedfiles, alsorestore
359 359
360 360
361 361 def parsealiases(cmd):
362 362 base_aliases = cmd.split(b"|")
363 363 all_aliases = set(base_aliases)
364 364 extra_aliases = []
365 365 for alias in base_aliases:
366 366 if b'-' in alias:
367 367 folded_alias = alias.replace(b'-', b'')
368 368 if folded_alias not in all_aliases:
369 369 all_aliases.add(folded_alias)
370 370 extra_aliases.append(folded_alias)
371 371 base_aliases.extend(extra_aliases)
372 372 return base_aliases
373 373
374 374
375 375 def setupwrapcolorwrite(ui):
376 376 # wrap ui.write so diff output can be labeled/colorized
377 377 def wrapwrite(orig, *args, **kw):
378 378 label = kw.pop('label', b'')
379 379 for chunk, l in patch.difflabel(lambda: args):
380 380 orig(chunk, label=label + l)
381 381
382 382 oldwrite = ui.write
383 383
384 384 def wrap(*args, **kwargs):
385 385 return wrapwrite(oldwrite, *args, **kwargs)
386 386
387 387 setattr(ui, 'write', wrap)
388 388 return oldwrite
389 389
390 390
391 391 def filterchunks(ui, originalhunks, usecurses, testfile, match, operation=None):
392 392 try:
393 393 if usecurses:
394 394 if testfile:
395 395 recordfn = crecordmod.testdecorator(
396 396 testfile, crecordmod.testchunkselector
397 397 )
398 398 else:
399 399 recordfn = crecordmod.chunkselector
400 400
401 401 return crecordmod.filterpatch(
402 402 ui, originalhunks, recordfn, operation
403 403 )
404 404 except crecordmod.fallbackerror as e:
405 405 ui.warn(b'%s\n' % e)
406 406 ui.warn(_(b'falling back to text mode\n'))
407 407
408 408 return patch.filterpatch(ui, originalhunks, match, operation)
409 409
410 410
411 411 def recordfilter(ui, originalhunks, match, operation=None):
412 412 """Prompts the user to filter the originalhunks and return a list of
413 413 selected hunks.
414 414 *operation* is used for to build ui messages to indicate the user what
415 415 kind of filtering they are doing: reverting, committing, shelving, etc.
416 416 (see patch.filterpatch).
417 417 """
418 418 usecurses = crecordmod.checkcurses(ui)
419 419 testfile = ui.config(b'experimental', b'crecordtest')
420 420 oldwrite = setupwrapcolorwrite(ui)
421 421 try:
422 422 newchunks, newopts = filterchunks(
423 423 ui, originalhunks, usecurses, testfile, match, operation
424 424 )
425 425 finally:
426 426 ui.write = oldwrite
427 427 return newchunks, newopts
428 428
429 429
430 430 def dorecord(
431 431 ui, repo, commitfunc, cmdsuggest, backupall, filterfn, *pats, **opts
432 432 ):
433 433 opts = pycompat.byteskwargs(opts)
434 434 if not ui.interactive():
435 435 if cmdsuggest:
436 436 msg = _(b'running non-interactively, use %s instead') % cmdsuggest
437 437 else:
438 438 msg = _(b'running non-interactively')
439 439 raise error.InputError(msg)
440 440
441 441 # make sure username is set before going interactive
442 442 if not opts.get(b'user'):
443 443 ui.username() # raise exception, username not provided
444 444
445 445 def recordfunc(ui, repo, message, match, opts):
446 446 """This is generic record driver.
447 447
448 448 Its job is to interactively filter local changes, and
449 449 accordingly prepare working directory into a state in which the
450 450 job can be delegated to a non-interactive commit command such as
451 451 'commit' or 'qrefresh'.
452 452
453 453 After the actual job is done by non-interactive command, the
454 454 working directory is restored to its original state.
455 455
456 456 In the end we'll record interesting changes, and everything else
457 457 will be left in place, so the user can continue working.
458 458 """
459 459 if not opts.get(b'interactive-unshelve'):
460 460 checkunfinished(repo, commit=True)
461 461 wctx = repo[None]
462 462 merge = len(wctx.parents()) > 1
463 463 if merge:
464 464 raise error.InputError(
465 465 _(
466 466 b'cannot partially commit a merge '
467 467 b'(use "hg commit" instead)'
468 468 )
469 469 )
470 470
471 471 def fail(f, msg):
472 472 raise error.InputError(b'%s: %s' % (f, msg))
473 473
474 474 force = opts.get(b'force')
475 475 if not force:
476 476 match = matchmod.badmatch(match, fail)
477 477
478 478 status = repo.status(match=match)
479 479
480 480 overrides = {(b'ui', b'commitsubrepos'): True}
481 481
482 482 with repo.ui.configoverride(overrides, b'record'):
483 483 # subrepoutil.precommit() modifies the status
484 484 tmpstatus = scmutil.status(
485 485 copymod.copy(status.modified),
486 486 copymod.copy(status.added),
487 487 copymod.copy(status.removed),
488 488 copymod.copy(status.deleted),
489 489 copymod.copy(status.unknown),
490 490 copymod.copy(status.ignored),
491 491 copymod.copy(status.clean), # pytype: disable=wrong-arg-count
492 492 )
493 493
494 494 # Force allows -X subrepo to skip the subrepo.
495 495 subs, commitsubs, newstate = subrepoutil.precommit(
496 496 repo.ui, wctx, tmpstatus, match, force=True
497 497 )
498 498 for s in subs:
499 499 if s in commitsubs:
500 500 dirtyreason = wctx.sub(s).dirtyreason(True)
501 501 raise error.Abort(dirtyreason)
502 502
503 503 if not force:
504 504 repo.checkcommitpatterns(wctx, match, status, fail)
505 505 diffopts = patch.difffeatureopts(
506 506 ui,
507 507 opts=opts,
508 508 whitespace=True,
509 509 section=b'commands',
510 510 configprefix=b'commit.interactive.',
511 511 )
512 512 diffopts.nodates = True
513 513 diffopts.git = True
514 514 diffopts.showfunc = True
515 515 originaldiff = patch.diff(repo, changes=status, opts=diffopts)
516 516 originalchunks = patch.parsepatch(originaldiff)
517 517 match = scmutil.match(repo[None], pats)
518 518
519 519 # 1. filter patch, since we are intending to apply subset of it
520 520 try:
521 521 chunks, newopts = filterfn(ui, originalchunks, match)
522 522 except error.PatchError as err:
523 523 raise error.InputError(_(b'error parsing patch: %s') % err)
524 524 opts.update(newopts)
525 525
526 526 # We need to keep a backup of files that have been newly added and
527 527 # modified during the recording process because there is a previous
528 528 # version without the edit in the workdir. We also will need to restore
529 529 # files that were the sources of renames so that the patch application
530 530 # works.
531 531 newlyaddedandmodifiedfiles, alsorestore = newandmodified(
532 532 chunks, originalchunks
533 533 )
534 534 contenders = set()
535 535 for h in chunks:
536 536 try:
537 537 contenders.update(set(h.files()))
538 538 except AttributeError:
539 539 pass
540 540
541 541 changed = status.modified + status.added + status.removed
542 542 newfiles = [f for f in changed if f in contenders]
543 543 if not newfiles:
544 544 ui.status(_(b'no changes to record\n'))
545 545 return 0
546 546
547 547 modified = set(status.modified)
548 548
549 549 # 2. backup changed files, so we can restore them in the end
550 550
551 551 if backupall:
552 552 tobackup = changed
553 553 else:
554 554 tobackup = [
555 555 f
556 556 for f in newfiles
557 557 if f in modified or f in newlyaddedandmodifiedfiles
558 558 ]
559 559 backups = {}
560 560 if tobackup:
561 561 backupdir = repo.vfs.join(b'record-backups')
562 562 try:
563 563 os.mkdir(backupdir)
564 564 except OSError as err:
565 565 if err.errno != errno.EEXIST:
566 566 raise
567 567 try:
568 568 # backup continues
569 569 for f in tobackup:
570 570 fd, tmpname = pycompat.mkstemp(
571 571 prefix=os.path.basename(f) + b'.', dir=backupdir
572 572 )
573 573 os.close(fd)
574 574 ui.debug(b'backup %r as %r\n' % (f, tmpname))
575 575 util.copyfile(repo.wjoin(f), tmpname, copystat=True)
576 576 backups[f] = tmpname
577 577
578 578 fp = stringio()
579 579 for c in chunks:
580 580 fname = c.filename()
581 581 if fname in backups:
582 582 c.write(fp)
583 583 dopatch = fp.tell()
584 584 fp.seek(0)
585 585
586 586 # 2.5 optionally review / modify patch in text editor
587 587 if opts.get(b'review', False):
588 588 patchtext = (
589 589 crecordmod.diffhelptext
590 590 + crecordmod.patchhelptext
591 591 + fp.read()
592 592 )
593 593 reviewedpatch = ui.edit(
594 594 patchtext, b"", action=b"diff", repopath=repo.path
595 595 )
596 596 fp.truncate(0)
597 597 fp.write(reviewedpatch)
598 598 fp.seek(0)
599 599
600 600 [os.unlink(repo.wjoin(c)) for c in newlyaddedandmodifiedfiles]
601 601 # 3a. apply filtered patch to clean repo (clean)
602 602 if backups:
603 603 m = scmutil.matchfiles(repo, set(backups.keys()) | alsorestore)
604 604 mergemod.revert_to(repo[b'.'], matcher=m)
605 605
606 606 # 3b. (apply)
607 607 if dopatch:
608 608 try:
609 609 ui.debug(b'applying patch\n')
610 610 ui.debug(fp.getvalue())
611 611 patch.internalpatch(ui, repo, fp, 1, eolmode=None)
612 612 except error.PatchError as err:
613 613 raise error.InputError(pycompat.bytestr(err))
614 614 del fp
615 615
616 616 # 4. We prepared working directory according to filtered
617 617 # patch. Now is the time to delegate the job to
618 618 # commit/qrefresh or the like!
619 619
620 620 # Make all of the pathnames absolute.
621 621 newfiles = [repo.wjoin(nf) for nf in newfiles]
622 622 return commitfunc(ui, repo, *newfiles, **pycompat.strkwargs(opts))
623 623 finally:
624 624 # 5. finally restore backed-up files
625 625 try:
626 626 dirstate = repo.dirstate
627 627 for realname, tmpname in pycompat.iteritems(backups):
628 628 ui.debug(b'restoring %r to %r\n' % (tmpname, realname))
629 629
630 630 if dirstate[realname] == b'n':
631 631 # without normallookup, restoring timestamp
632 632 # may cause partially committed files
633 633 # to be treated as unmodified
634 634 dirstate.normallookup(realname)
635 635
636 636 # copystat=True here and above are a hack to trick any
637 637 # editors that have f open that we haven't modified them.
638 638 #
639 639 # Also note that this racy as an editor could notice the
640 640 # file's mtime before we've finished writing it.
641 641 util.copyfile(tmpname, repo.wjoin(realname), copystat=True)
642 642 os.unlink(tmpname)
643 643 if tobackup:
644 644 os.rmdir(backupdir)
645 645 except OSError:
646 646 pass
647 647
648 648 def recordinwlock(ui, repo, message, match, opts):
649 649 with repo.wlock():
650 650 return recordfunc(ui, repo, message, match, opts)
651 651
652 652 return commit(ui, repo, recordinwlock, pats, opts)
653 653
654 654
655 655 class dirnode(object):
656 656 """
657 657 Represent a directory in user working copy with information required for
658 658 the purpose of tersing its status.
659 659
660 660 path is the path to the directory, without a trailing '/'
661 661
662 662 statuses is a set of statuses of all files in this directory (this includes
663 663 all the files in all the subdirectories too)
664 664
665 665 files is a list of files which are direct child of this directory
666 666
667 667 subdirs is a dictionary of sub-directory name as the key and it's own
668 668 dirnode object as the value
669 669 """
670 670
671 671 def __init__(self, dirpath):
672 672 self.path = dirpath
673 673 self.statuses = set()
674 674 self.files = []
675 675 self.subdirs = {}
676 676
677 677 def _addfileindir(self, filename, status):
678 678 """Add a file in this directory as a direct child."""
679 679 self.files.append((filename, status))
680 680
681 681 def addfile(self, filename, status):
682 682 """
683 683 Add a file to this directory or to its direct parent directory.
684 684
685 685 If the file is not direct child of this directory, we traverse to the
686 686 directory of which this file is a direct child of and add the file
687 687 there.
688 688 """
689 689
690 690 # the filename contains a path separator, it means it's not the direct
691 691 # child of this directory
692 692 if b'/' in filename:
693 693 subdir, filep = filename.split(b'/', 1)
694 694
695 695 # does the dirnode object for subdir exists
696 696 if subdir not in self.subdirs:
697 697 subdirpath = pathutil.join(self.path, subdir)
698 698 self.subdirs[subdir] = dirnode(subdirpath)
699 699
700 700 # try adding the file in subdir
701 701 self.subdirs[subdir].addfile(filep, status)
702 702
703 703 else:
704 704 self._addfileindir(filename, status)
705 705
706 706 if status not in self.statuses:
707 707 self.statuses.add(status)
708 708
709 709 def iterfilepaths(self):
710 710 """Yield (status, path) for files directly under this directory."""
711 711 for f, st in self.files:
712 712 yield st, pathutil.join(self.path, f)
713 713
714 714 def tersewalk(self, terseargs):
715 715 """
716 716 Yield (status, path) obtained by processing the status of this
717 717 dirnode.
718 718
719 719 terseargs is the string of arguments passed by the user with `--terse`
720 720 flag.
721 721
722 722 Following are the cases which can happen:
723 723
724 724 1) All the files in the directory (including all the files in its
725 725 subdirectories) share the same status and the user has asked us to terse
726 726 that status. -> yield (status, dirpath). dirpath will end in '/'.
727 727
728 728 2) Otherwise, we do following:
729 729
730 730 a) Yield (status, filepath) for all the files which are in this
731 731 directory (only the ones in this directory, not the subdirs)
732 732
733 733 b) Recurse the function on all the subdirectories of this
734 734 directory
735 735 """
736 736
737 737 if len(self.statuses) == 1:
738 738 onlyst = self.statuses.pop()
739 739
740 740 # Making sure we terse only when the status abbreviation is
741 741 # passed as terse argument
742 742 if onlyst in terseargs:
743 743 yield onlyst, self.path + b'/'
744 744 return
745 745
746 746 # add the files to status list
747 747 for st, fpath in self.iterfilepaths():
748 748 yield st, fpath
749 749
750 750 # recurse on the subdirs
751 751 for dirobj in self.subdirs.values():
752 752 for st, fpath in dirobj.tersewalk(terseargs):
753 753 yield st, fpath
754 754
755 755
756 756 def tersedir(statuslist, terseargs):
757 757 """
758 758 Terse the status if all the files in a directory shares the same status.
759 759
760 760 statuslist is scmutil.status() object which contains a list of files for
761 761 each status.
762 762 terseargs is string which is passed by the user as the argument to `--terse`
763 763 flag.
764 764
765 765 The function makes a tree of objects of dirnode class, and at each node it
766 766 stores the information required to know whether we can terse a certain
767 767 directory or not.
768 768 """
769 769 # the order matters here as that is used to produce final list
770 770 allst = (b'm', b'a', b'r', b'd', b'u', b'i', b'c')
771 771
772 772 # checking the argument validity
773 773 for s in pycompat.bytestr(terseargs):
774 774 if s not in allst:
775 775 raise error.InputError(_(b"'%s' not recognized") % s)
776 776
777 777 # creating a dirnode object for the root of the repo
778 778 rootobj = dirnode(b'')
779 779 pstatus = (
780 780 b'modified',
781 781 b'added',
782 782 b'deleted',
783 783 b'clean',
784 784 b'unknown',
785 785 b'ignored',
786 786 b'removed',
787 787 )
788 788
789 789 tersedict = {}
790 790 for attrname in pstatus:
791 791 statuschar = attrname[0:1]
792 792 for f in getattr(statuslist, attrname):
793 793 rootobj.addfile(f, statuschar)
794 794 tersedict[statuschar] = []
795 795
796 796 # we won't be tersing the root dir, so add files in it
797 797 for st, fpath in rootobj.iterfilepaths():
798 798 tersedict[st].append(fpath)
799 799
800 800 # process each sub-directory and build tersedict
801 801 for subdir in rootobj.subdirs.values():
802 802 for st, f in subdir.tersewalk(terseargs):
803 803 tersedict[st].append(f)
804 804
805 805 tersedlist = []
806 806 for st in allst:
807 807 tersedict[st].sort()
808 808 tersedlist.append(tersedict[st])
809 809
810 810 return scmutil.status(*tersedlist)
811 811
812 812
813 813 def _commentlines(raw):
814 814 '''Surround lineswith a comment char and a new line'''
815 815 lines = raw.splitlines()
816 816 commentedlines = [b'# %s' % line for line in lines]
817 817 return b'\n'.join(commentedlines) + b'\n'
818 818
819 819
820 820 @attr.s(frozen=True)
821 821 class morestatus(object):
822 822 reporoot = attr.ib()
823 823 unfinishedop = attr.ib()
824 824 unfinishedmsg = attr.ib()
825 825 activemerge = attr.ib()
826 826 unresolvedpaths = attr.ib()
827 827 _formattedpaths = attr.ib(init=False, default=set())
828 828 _label = b'status.morestatus'
829 829
830 830 def formatfile(self, path, fm):
831 831 self._formattedpaths.add(path)
832 832 if self.activemerge and path in self.unresolvedpaths:
833 833 fm.data(unresolved=True)
834 834
835 835 def formatfooter(self, fm):
836 836 if self.unfinishedop or self.unfinishedmsg:
837 837 fm.startitem()
838 838 fm.data(itemtype=b'morestatus')
839 839
840 840 if self.unfinishedop:
841 841 fm.data(unfinished=self.unfinishedop)
842 842 statemsg = (
843 843 _(b'The repository is in an unfinished *%s* state.')
844 844 % self.unfinishedop
845 845 )
846 846 fm.plain(b'%s\n' % _commentlines(statemsg), label=self._label)
847 847 if self.unfinishedmsg:
848 848 fm.data(unfinishedmsg=self.unfinishedmsg)
849 849
850 850 # May also start new data items.
851 851 self._formatconflicts(fm)
852 852
853 853 if self.unfinishedmsg:
854 854 fm.plain(
855 855 b'%s\n' % _commentlines(self.unfinishedmsg), label=self._label
856 856 )
857 857
858 858 def _formatconflicts(self, fm):
859 859 if not self.activemerge:
860 860 return
861 861
862 862 if self.unresolvedpaths:
863 863 mergeliststr = b'\n'.join(
864 864 [
865 865 b' %s'
866 866 % util.pathto(self.reporoot, encoding.getcwd(), path)
867 867 for path in self.unresolvedpaths
868 868 ]
869 869 )
870 870 msg = (
871 871 _(
872 872 '''Unresolved merge conflicts:
873 873
874 874 %s
875 875
876 876 To mark files as resolved: hg resolve --mark FILE'''
877 877 )
878 878 % mergeliststr
879 879 )
880 880
881 881 # If any paths with unresolved conflicts were not previously
882 882 # formatted, output them now.
883 883 for f in self.unresolvedpaths:
884 884 if f in self._formattedpaths:
885 885 # Already output.
886 886 continue
887 887 fm.startitem()
888 888 # We can't claim to know the status of the file - it may just
889 889 # have been in one of the states that were not requested for
890 890 # display, so it could be anything.
891 891 fm.data(itemtype=b'file', path=f, unresolved=True)
892 892
893 893 else:
894 894 msg = _(b'No unresolved merge conflicts.')
895 895
896 896 fm.plain(b'%s\n' % _commentlines(msg), label=self._label)
897 897
898 898
899 899 def readmorestatus(repo):
900 900 """Returns a morestatus object if the repo has unfinished state."""
901 901 statetuple = statemod.getrepostate(repo)
902 902 mergestate = mergestatemod.mergestate.read(repo)
903 903 activemerge = mergestate.active()
904 904 if not statetuple and not activemerge:
905 905 return None
906 906
907 907 unfinishedop = unfinishedmsg = unresolved = None
908 908 if statetuple:
909 909 unfinishedop, unfinishedmsg = statetuple
910 910 if activemerge:
911 911 unresolved = sorted(mergestate.unresolved())
912 912 return morestatus(
913 913 repo.root, unfinishedop, unfinishedmsg, activemerge, unresolved
914 914 )
915 915
916 916
917 917 def findpossible(cmd, table, strict=False):
918 918 """
919 919 Return cmd -> (aliases, command table entry)
920 920 for each matching command.
921 921 Return debug commands (or their aliases) only if no normal command matches.
922 922 """
923 923 choice = {}
924 924 debugchoice = {}
925 925
926 926 if cmd in table:
927 927 # short-circuit exact matches, "log" alias beats "log|history"
928 928 keys = [cmd]
929 929 else:
930 930 keys = table.keys()
931 931
932 932 allcmds = []
933 933 for e in keys:
934 934 aliases = parsealiases(e)
935 935 allcmds.extend(aliases)
936 936 found = None
937 937 if cmd in aliases:
938 938 found = cmd
939 939 elif not strict:
940 940 for a in aliases:
941 941 if a.startswith(cmd):
942 942 found = a
943 943 break
944 944 if found is not None:
945 945 if aliases[0].startswith(b"debug") or found.startswith(b"debug"):
946 946 debugchoice[found] = (aliases, table[e])
947 947 else:
948 948 choice[found] = (aliases, table[e])
949 949
950 950 if not choice and debugchoice:
951 951 choice = debugchoice
952 952
953 953 return choice, allcmds
954 954
955 955
956 956 def findcmd(cmd, table, strict=True):
957 957 """Return (aliases, command table entry) for command string."""
958 958 choice, allcmds = findpossible(cmd, table, strict)
959 959
960 960 if cmd in choice:
961 961 return choice[cmd]
962 962
963 963 if len(choice) > 1:
964 964 clist = sorted(choice)
965 965 raise error.AmbiguousCommand(cmd, clist)
966 966
967 967 if choice:
968 968 return list(choice.values())[0]
969 969
970 970 raise error.UnknownCommand(cmd, allcmds)
971 971
972 972
973 973 def changebranch(ui, repo, revs, label, opts):
974 974 """ Change the branch name of given revs to label """
975 975
976 976 with repo.wlock(), repo.lock(), repo.transaction(b'branches'):
977 977 # abort in case of uncommitted merge or dirty wdir
978 978 bailifchanged(repo)
979 979 revs = scmutil.revrange(repo, revs)
980 980 if not revs:
981 981 raise error.InputError(b"empty revision set")
982 982 roots = repo.revs(b'roots(%ld)', revs)
983 983 if len(roots) > 1:
984 984 raise error.InputError(
985 985 _(b"cannot change branch of non-linear revisions")
986 986 )
987 987 rewriteutil.precheck(repo, revs, b'change branch of')
988 988
989 989 root = repo[roots.first()]
990 990 rpb = {parent.branch() for parent in root.parents()}
991 991 if (
992 992 not opts.get(b'force')
993 993 and label not in rpb
994 994 and label in repo.branchmap()
995 995 ):
996 996 raise error.InputError(
997 997 _(b"a branch of the same name already exists")
998 998 )
999 999
1000 1000 if repo.revs(b'obsolete() and %ld', revs):
1001 1001 raise error.InputError(
1002 1002 _(b"cannot change branch of a obsolete changeset")
1003 1003 )
1004 1004
1005 1005 # make sure only topological heads
1006 1006 if repo.revs(b'heads(%ld) - head()', revs):
1007 1007 raise error.InputError(
1008 1008 _(b"cannot change branch in middle of a stack")
1009 1009 )
1010 1010
1011 1011 replacements = {}
1012 1012 # avoid import cycle mercurial.cmdutil -> mercurial.context ->
1013 1013 # mercurial.subrepo -> mercurial.cmdutil
1014 1014 from . import context
1015 1015
1016 1016 for rev in revs:
1017 1017 ctx = repo[rev]
1018 1018 oldbranch = ctx.branch()
1019 1019 # check if ctx has same branch
1020 1020 if oldbranch == label:
1021 1021 continue
1022 1022
1023 1023 def filectxfn(repo, newctx, path):
1024 1024 try:
1025 1025 return ctx[path]
1026 1026 except error.ManifestLookupError:
1027 1027 return None
1028 1028
1029 1029 ui.debug(
1030 1030 b"changing branch of '%s' from '%s' to '%s'\n"
1031 1031 % (hex(ctx.node()), oldbranch, label)
1032 1032 )
1033 1033 extra = ctx.extra()
1034 1034 extra[b'branch_change'] = hex(ctx.node())
1035 1035 # While changing branch of set of linear commits, make sure that
1036 1036 # we base our commits on new parent rather than old parent which
1037 1037 # was obsoleted while changing the branch
1038 1038 p1 = ctx.p1().node()
1039 1039 p2 = ctx.p2().node()
1040 1040 if p1 in replacements:
1041 1041 p1 = replacements[p1][0]
1042 1042 if p2 in replacements:
1043 1043 p2 = replacements[p2][0]
1044 1044
1045 1045 mc = context.memctx(
1046 1046 repo,
1047 1047 (p1, p2),
1048 1048 ctx.description(),
1049 1049 ctx.files(),
1050 1050 filectxfn,
1051 1051 user=ctx.user(),
1052 1052 date=ctx.date(),
1053 1053 extra=extra,
1054 1054 branch=label,
1055 1055 )
1056 1056
1057 1057 newnode = repo.commitctx(mc)
1058 1058 replacements[ctx.node()] = (newnode,)
1059 1059 ui.debug(b'new node id is %s\n' % hex(newnode))
1060 1060
1061 1061 # create obsmarkers and move bookmarks
1062 1062 scmutil.cleanupnodes(
1063 1063 repo, replacements, b'branch-change', fixphase=True
1064 1064 )
1065 1065
1066 1066 # move the working copy too
1067 1067 wctx = repo[None]
1068 1068 # in-progress merge is a bit too complex for now.
1069 1069 if len(wctx.parents()) == 1:
1070 1070 newid = replacements.get(wctx.p1().node())
1071 1071 if newid is not None:
1072 1072 # avoid import cycle mercurial.cmdutil -> mercurial.hg ->
1073 1073 # mercurial.cmdutil
1074 1074 from . import hg
1075 1075
1076 1076 hg.update(repo, newid[0], quietempty=True)
1077 1077
1078 1078 ui.status(_(b"changed branch on %d changesets\n") % len(replacements))
1079 1079
1080 1080
1081 1081 def findrepo(p):
1082 1082 while not os.path.isdir(os.path.join(p, b".hg")):
1083 1083 oldp, p = p, os.path.dirname(p)
1084 1084 if p == oldp:
1085 1085 return None
1086 1086
1087 1087 return p
1088 1088
1089 1089
1090 1090 def bailifchanged(repo, merge=True, hint=None):
1091 1091 """enforce the precondition that working directory must be clean.
1092 1092
1093 1093 'merge' can be set to false if a pending uncommitted merge should be
1094 1094 ignored (such as when 'update --check' runs).
1095 1095
1096 1096 'hint' is the usual hint given to Abort exception.
1097 1097 """
1098 1098
1099 1099 if merge and repo.dirstate.p2() != nullid:
1100 1100 raise error.StateError(_(b'outstanding uncommitted merge'), hint=hint)
1101 1101 st = repo.status()
1102 1102 if st.modified or st.added or st.removed or st.deleted:
1103 1103 raise error.StateError(_(b'uncommitted changes'), hint=hint)
1104 1104 ctx = repo[None]
1105 1105 for s in sorted(ctx.substate):
1106 1106 ctx.sub(s).bailifchanged(hint=hint)
1107 1107
1108 1108
1109 1109 def logmessage(ui, opts):
1110 1110 """ get the log message according to -m and -l option """
1111 1111
1112 1112 check_at_most_one_arg(opts, b'message', b'logfile')
1113 1113
1114 1114 message = opts.get(b'message')
1115 1115 logfile = opts.get(b'logfile')
1116 1116
1117 1117 if not message and logfile:
1118 1118 try:
1119 1119 if isstdiofilename(logfile):
1120 1120 message = ui.fin.read()
1121 1121 else:
1122 1122 message = b'\n'.join(util.readfile(logfile).splitlines())
1123 1123 except IOError as inst:
1124 1124 raise error.Abort(
1125 1125 _(b"can't read commit message '%s': %s")
1126 1126 % (logfile, encoding.strtolocal(inst.strerror))
1127 1127 )
1128 1128 return message
1129 1129
1130 1130
1131 1131 def mergeeditform(ctxorbool, baseformname):
1132 1132 """return appropriate editform name (referencing a committemplate)
1133 1133
1134 1134 'ctxorbool' is either a ctx to be committed, or a bool indicating whether
1135 1135 merging is committed.
1136 1136
1137 1137 This returns baseformname with '.merge' appended if it is a merge,
1138 1138 otherwise '.normal' is appended.
1139 1139 """
1140 1140 if isinstance(ctxorbool, bool):
1141 1141 if ctxorbool:
1142 1142 return baseformname + b".merge"
1143 1143 elif len(ctxorbool.parents()) > 1:
1144 1144 return baseformname + b".merge"
1145 1145
1146 1146 return baseformname + b".normal"
1147 1147
1148 1148
1149 1149 def getcommiteditor(
1150 1150 edit=False, finishdesc=None, extramsg=None, editform=b'', **opts
1151 1151 ):
1152 1152 """get appropriate commit message editor according to '--edit' option
1153 1153
1154 1154 'finishdesc' is a function to be called with edited commit message
1155 1155 (= 'description' of the new changeset) just after editing, but
1156 1156 before checking empty-ness. It should return actual text to be
1157 1157 stored into history. This allows to change description before
1158 1158 storing.
1159 1159
1160 1160 'extramsg' is a extra message to be shown in the editor instead of
1161 1161 'Leave message empty to abort commit' line. 'HG: ' prefix and EOL
1162 1162 is automatically added.
1163 1163
1164 1164 'editform' is a dot-separated list of names, to distinguish
1165 1165 the purpose of commit text editing.
1166 1166
1167 1167 'getcommiteditor' returns 'commitforceeditor' regardless of
1168 1168 'edit', if one of 'finishdesc' or 'extramsg' is specified, because
1169 1169 they are specific for usage in MQ.
1170 1170 """
1171 1171 if edit or finishdesc or extramsg:
1172 1172 return lambda r, c, s: commitforceeditor(
1173 1173 r, c, s, finishdesc=finishdesc, extramsg=extramsg, editform=editform
1174 1174 )
1175 1175 elif editform:
1176 1176 return lambda r, c, s: commiteditor(r, c, s, editform=editform)
1177 1177 else:
1178 1178 return commiteditor
1179 1179
1180 1180
1181 1181 def _escapecommandtemplate(tmpl):
1182 1182 parts = []
1183 1183 for typ, start, end in templater.scantemplate(tmpl, raw=True):
1184 1184 if typ == b'string':
1185 1185 parts.append(stringutil.escapestr(tmpl[start:end]))
1186 1186 else:
1187 1187 parts.append(tmpl[start:end])
1188 1188 return b''.join(parts)
1189 1189
1190 1190
1191 1191 def rendercommandtemplate(ui, tmpl, props):
1192 1192 r"""Expand a literal template 'tmpl' in a way suitable for command line
1193 1193
1194 1194 '\' in outermost string is not taken as an escape character because it
1195 1195 is a directory separator on Windows.
1196 1196
1197 1197 >>> from . import ui as uimod
1198 1198 >>> ui = uimod.ui()
1199 1199 >>> rendercommandtemplate(ui, b'c:\\{path}', {b'path': b'foo'})
1200 1200 'c:\\foo'
1201 1201 >>> rendercommandtemplate(ui, b'{"c:\\{path}"}', {'path': b'foo'})
1202 1202 'c:{path}'
1203 1203 """
1204 1204 if not tmpl:
1205 1205 return tmpl
1206 1206 t = formatter.maketemplater(ui, _escapecommandtemplate(tmpl))
1207 1207 return t.renderdefault(props)
1208 1208
1209 1209
1210 1210 def rendertemplate(ctx, tmpl, props=None):
1211 1211 """Expand a literal template 'tmpl' byte-string against one changeset
1212 1212
1213 1213 Each props item must be a stringify-able value or a callable returning
1214 1214 such value, i.e. no bare list nor dict should be passed.
1215 1215 """
1216 1216 repo = ctx.repo()
1217 1217 tres = formatter.templateresources(repo.ui, repo)
1218 1218 t = formatter.maketemplater(
1219 1219 repo.ui, tmpl, defaults=templatekw.keywords, resources=tres
1220 1220 )
1221 1221 mapping = {b'ctx': ctx}
1222 1222 if props:
1223 1223 mapping.update(props)
1224 1224 return t.renderdefault(mapping)
1225 1225
1226 1226
1227 1227 def format_changeset_summary(ui, ctx, command=None, default_spec=None):
1228 1228 """Format a changeset summary (one line)."""
1229 1229 spec = None
1230 1230 if command:
1231 1231 spec = ui.config(
1232 1232 b'command-templates', b'oneline-summary.%s' % command, None
1233 1233 )
1234 1234 if not spec:
1235 1235 spec = ui.config(b'command-templates', b'oneline-summary')
1236 1236 if not spec:
1237 1237 spec = default_spec
1238 1238 if not spec:
1239 1239 spec = (
1240 1240 b'{separate(" ", '
1241 1241 b'label("oneline-summary.changeset", "{rev}:{node|short}")'
1242 1242 b', '
1243 1243 b'join(filter(namespaces % "{ifeq(namespace, "branches", "", join(names % "{label("oneline-summary.{namespace}", name)}", " "))}"), " ")'
1244 1244 b')} '
1245 1245 b'"{label("oneline-summary.desc", desc|firstline)}"'
1246 1246 )
1247 1247 text = rendertemplate(ctx, spec)
1248 1248 return text.split(b'\n')[0]
1249 1249
1250 1250
1251 1251 def _buildfntemplate(pat, total=None, seqno=None, revwidth=None, pathname=None):
1252 1252 r"""Convert old-style filename format string to template string
1253 1253
1254 1254 >>> _buildfntemplate(b'foo-%b-%n.patch', seqno=0)
1255 1255 'foo-{reporoot|basename}-{seqno}.patch'
1256 1256 >>> _buildfntemplate(b'%R{tags % "{tag}"}%H')
1257 1257 '{rev}{tags % "{tag}"}{node}'
1258 1258
1259 1259 '\' in outermost strings has to be escaped because it is a directory
1260 1260 separator on Windows:
1261 1261
1262 1262 >>> _buildfntemplate(b'c:\\tmp\\%R\\%n.patch', seqno=0)
1263 1263 'c:\\\\tmp\\\\{rev}\\\\{seqno}.patch'
1264 1264 >>> _buildfntemplate(b'\\\\foo\\bar.patch')
1265 1265 '\\\\\\\\foo\\\\bar.patch'
1266 1266 >>> _buildfntemplate(b'\\{tags % "{tag}"}')
1267 1267 '\\\\{tags % "{tag}"}'
1268 1268
1269 1269 but inner strings follow the template rules (i.e. '\' is taken as an
1270 1270 escape character):
1271 1271
1272 1272 >>> _buildfntemplate(br'{"c:\tmp"}', seqno=0)
1273 1273 '{"c:\\tmp"}'
1274 1274 """
1275 1275 expander = {
1276 1276 b'H': b'{node}',
1277 1277 b'R': b'{rev}',
1278 1278 b'h': b'{node|short}',
1279 1279 b'm': br'{sub(r"[^\w]", "_", desc|firstline)}',
1280 1280 b'r': b'{if(revwidth, pad(rev, revwidth, "0", left=True), rev)}',
1281 1281 b'%': b'%',
1282 1282 b'b': b'{reporoot|basename}',
1283 1283 }
1284 1284 if total is not None:
1285 1285 expander[b'N'] = b'{total}'
1286 1286 if seqno is not None:
1287 1287 expander[b'n'] = b'{seqno}'
1288 1288 if total is not None and seqno is not None:
1289 1289 expander[b'n'] = b'{pad(seqno, total|stringify|count, "0", left=True)}'
1290 1290 if pathname is not None:
1291 1291 expander[b's'] = b'{pathname|basename}'
1292 1292 expander[b'd'] = b'{if(pathname|dirname, pathname|dirname, ".")}'
1293 1293 expander[b'p'] = b'{pathname}'
1294 1294
1295 1295 newname = []
1296 1296 for typ, start, end in templater.scantemplate(pat, raw=True):
1297 1297 if typ != b'string':
1298 1298 newname.append(pat[start:end])
1299 1299 continue
1300 1300 i = start
1301 1301 while i < end:
1302 1302 n = pat.find(b'%', i, end)
1303 1303 if n < 0:
1304 1304 newname.append(stringutil.escapestr(pat[i:end]))
1305 1305 break
1306 1306 newname.append(stringutil.escapestr(pat[i:n]))
1307 1307 if n + 2 > end:
1308 1308 raise error.Abort(
1309 1309 _(b"incomplete format spec in output filename")
1310 1310 )
1311 1311 c = pat[n + 1 : n + 2]
1312 1312 i = n + 2
1313 1313 try:
1314 1314 newname.append(expander[c])
1315 1315 except KeyError:
1316 1316 raise error.Abort(
1317 1317 _(b"invalid format spec '%%%s' in output filename") % c
1318 1318 )
1319 1319 return b''.join(newname)
1320 1320
1321 1321
1322 1322 def makefilename(ctx, pat, **props):
1323 1323 if not pat:
1324 1324 return pat
1325 1325 tmpl = _buildfntemplate(pat, **props)
1326 1326 # BUG: alias expansion shouldn't be made against template fragments
1327 1327 # rewritten from %-format strings, but we have no easy way to partially
1328 1328 # disable the expansion.
1329 1329 return rendertemplate(ctx, tmpl, pycompat.byteskwargs(props))
1330 1330
1331 1331
1332 1332 def isstdiofilename(pat):
1333 1333 """True if the given pat looks like a filename denoting stdin/stdout"""
1334 1334 return not pat or pat == b'-'
1335 1335
1336 1336
1337 1337 class _unclosablefile(object):
1338 1338 def __init__(self, fp):
1339 1339 self._fp = fp
1340 1340
1341 1341 def close(self):
1342 1342 pass
1343 1343
1344 1344 def __iter__(self):
1345 1345 return iter(self._fp)
1346 1346
1347 1347 def __getattr__(self, attr):
1348 1348 return getattr(self._fp, attr)
1349 1349
1350 1350 def __enter__(self):
1351 1351 return self
1352 1352
1353 1353 def __exit__(self, exc_type, exc_value, exc_tb):
1354 1354 pass
1355 1355
1356 1356
1357 1357 def makefileobj(ctx, pat, mode=b'wb', **props):
1358 1358 writable = mode not in (b'r', b'rb')
1359 1359
1360 1360 if isstdiofilename(pat):
1361 1361 repo = ctx.repo()
1362 1362 if writable:
1363 1363 fp = repo.ui.fout
1364 1364 else:
1365 1365 fp = repo.ui.fin
1366 1366 return _unclosablefile(fp)
1367 1367 fn = makefilename(ctx, pat, **props)
1368 1368 return open(fn, mode)
1369 1369
1370 1370
1371 1371 def openstorage(repo, cmd, file_, opts, returnrevlog=False):
1372 1372 """opens the changelog, manifest, a filelog or a given revlog"""
1373 1373 cl = opts[b'changelog']
1374 1374 mf = opts[b'manifest']
1375 1375 dir = opts[b'dir']
1376 1376 msg = None
1377 1377 if cl and mf:
1378 1378 msg = _(b'cannot specify --changelog and --manifest at the same time')
1379 1379 elif cl and dir:
1380 1380 msg = _(b'cannot specify --changelog and --dir at the same time')
1381 1381 elif cl or mf or dir:
1382 1382 if file_:
1383 1383 msg = _(b'cannot specify filename with --changelog or --manifest')
1384 1384 elif not repo:
1385 1385 msg = _(
1386 1386 b'cannot specify --changelog or --manifest or --dir '
1387 1387 b'without a repository'
1388 1388 )
1389 1389 if msg:
1390 1390 raise error.InputError(msg)
1391 1391
1392 1392 r = None
1393 1393 if repo:
1394 1394 if cl:
1395 1395 r = repo.unfiltered().changelog
1396 1396 elif dir:
1397 1397 if not scmutil.istreemanifest(repo):
1398 1398 raise error.InputError(
1399 1399 _(
1400 1400 b"--dir can only be used on repos with "
1401 1401 b"treemanifest enabled"
1402 1402 )
1403 1403 )
1404 1404 if not dir.endswith(b'/'):
1405 1405 dir = dir + b'/'
1406 1406 dirlog = repo.manifestlog.getstorage(dir)
1407 1407 if len(dirlog):
1408 1408 r = dirlog
1409 1409 elif mf:
1410 1410 r = repo.manifestlog.getstorage(b'')
1411 1411 elif file_:
1412 1412 filelog = repo.file(file_)
1413 1413 if len(filelog):
1414 1414 r = filelog
1415 1415
1416 1416 # Not all storage may be revlogs. If requested, try to return an actual
1417 1417 # revlog instance.
1418 1418 if returnrevlog:
1419 1419 if isinstance(r, revlog.revlog):
1420 1420 pass
1421 1421 elif util.safehasattr(r, b'_revlog'):
1422 1422 r = r._revlog # pytype: disable=attribute-error
1423 1423 elif r is not None:
1424 1424 raise error.InputError(
1425 1425 _(b'%r does not appear to be a revlog') % r
1426 1426 )
1427 1427
1428 1428 if not r:
1429 1429 if not returnrevlog:
1430 1430 raise error.InputError(_(b'cannot give path to non-revlog'))
1431 1431
1432 1432 if not file_:
1433 1433 raise error.CommandError(cmd, _(b'invalid arguments'))
1434 1434 if not os.path.isfile(file_):
1435 1435 raise error.InputError(_(b"revlog '%s' not found") % file_)
1436 1436 r = revlog.revlog(
1437 1437 vfsmod.vfs(encoding.getcwd(), audit=False), file_[:-2] + b".i"
1438 1438 )
1439 1439 return r
1440 1440
1441 1441
1442 1442 def openrevlog(repo, cmd, file_, opts):
1443 1443 """Obtain a revlog backing storage of an item.
1444 1444
1445 1445 This is similar to ``openstorage()`` except it always returns a revlog.
1446 1446
1447 1447 In most cases, a caller cares about the main storage object - not the
1448 1448 revlog backing it. Therefore, this function should only be used by code
1449 1449 that needs to examine low-level revlog implementation details. e.g. debug
1450 1450 commands.
1451 1451 """
1452 1452 return openstorage(repo, cmd, file_, opts, returnrevlog=True)
1453 1453
1454 1454
1455 1455 def copy(ui, repo, pats, opts, rename=False):
1456 1456 check_incompatible_arguments(opts, b'forget', [b'dry_run'])
1457 1457
1458 1458 # called with the repo lock held
1459 1459 #
1460 1460 # hgsep => pathname that uses "/" to separate directories
1461 1461 # ossep => pathname that uses os.sep to separate directories
1462 1462 cwd = repo.getcwd()
1463 1463 targets = {}
1464 1464 forget = opts.get(b"forget")
1465 1465 after = opts.get(b"after")
1466 1466 dryrun = opts.get(b"dry_run")
1467 1467 rev = opts.get(b'at_rev')
1468 1468 if rev:
1469 1469 if not forget and not after:
1470 1470 # TODO: Remove this restriction and make it also create the copy
1471 1471 # targets (and remove the rename source if rename==True).
1472 1472 raise error.InputError(_(b'--at-rev requires --after'))
1473 1473 ctx = scmutil.revsingle(repo, rev)
1474 1474 if len(ctx.parents()) > 1:
1475 1475 raise error.InputError(
1476 1476 _(b'cannot mark/unmark copy in merge commit')
1477 1477 )
1478 1478 else:
1479 1479 ctx = repo[None]
1480 1480
1481 1481 pctx = ctx.p1()
1482 1482
1483 1483 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
1484 1484
1485 1485 if forget:
1486 1486 if ctx.rev() is None:
1487 1487 new_ctx = ctx
1488 1488 else:
1489 1489 if len(ctx.parents()) > 1:
1490 1490 raise error.InputError(_(b'cannot unmark copy in merge commit'))
1491 1491 # avoid cycle context -> subrepo -> cmdutil
1492 1492 from . import context
1493 1493
1494 1494 rewriteutil.precheck(repo, [ctx.rev()], b'uncopy')
1495 1495 new_ctx = context.overlayworkingctx(repo)
1496 1496 new_ctx.setbase(ctx.p1())
1497 1497 mergemod.graft(repo, ctx, wctx=new_ctx)
1498 1498
1499 1499 match = scmutil.match(ctx, pats, opts)
1500 1500
1501 1501 current_copies = ctx.p1copies()
1502 1502 current_copies.update(ctx.p2copies())
1503 1503
1504 1504 uipathfn = scmutil.getuipathfn(repo)
1505 1505 for f in ctx.walk(match):
1506 1506 if f in current_copies:
1507 1507 new_ctx[f].markcopied(None)
1508 1508 elif match.exact(f):
1509 1509 ui.warn(
1510 1510 _(
1511 1511 b'%s: not unmarking as copy - file is not marked as copied\n'
1512 1512 )
1513 1513 % uipathfn(f)
1514 1514 )
1515 1515
1516 1516 if ctx.rev() is not None:
1517 1517 with repo.lock():
1518 1518 mem_ctx = new_ctx.tomemctx_for_amend(ctx)
1519 1519 new_node = mem_ctx.commit()
1520 1520
1521 1521 if repo.dirstate.p1() == ctx.node():
1522 1522 with repo.dirstate.parentchange():
1523 1523 scmutil.movedirstate(repo, repo[new_node])
1524 1524 replacements = {ctx.node(): [new_node]}
1525 1525 scmutil.cleanupnodes(
1526 1526 repo, replacements, b'uncopy', fixphase=True
1527 1527 )
1528 1528
1529 1529 return
1530 1530
1531 1531 pats = scmutil.expandpats(pats)
1532 1532 if not pats:
1533 1533 raise error.InputError(_(b'no source or destination specified'))
1534 1534 if len(pats) == 1:
1535 1535 raise error.InputError(_(b'no destination specified'))
1536 1536 dest = pats.pop()
1537 1537
1538 1538 def walkpat(pat):
1539 1539 srcs = []
1540 1540 # TODO: Inline and simplify the non-working-copy version of this code
1541 1541 # since it shares very little with the working-copy version of it.
1542 1542 ctx_to_walk = ctx if ctx.rev() is None else pctx
1543 1543 m = scmutil.match(ctx_to_walk, [pat], opts, globbed=True)
1544 1544 for abs in ctx_to_walk.walk(m):
1545 1545 rel = uipathfn(abs)
1546 1546 exact = m.exact(abs)
1547 1547 if abs not in ctx:
1548 1548 if abs in pctx:
1549 1549 if not after:
1550 1550 if exact:
1551 1551 ui.warn(
1552 1552 _(
1553 1553 b'%s: not copying - file has been marked '
1554 1554 b'for remove\n'
1555 1555 )
1556 1556 % rel
1557 1557 )
1558 1558 continue
1559 1559 else:
1560 1560 if exact:
1561 1561 ui.warn(
1562 1562 _(b'%s: not copying - file is not managed\n') % rel
1563 1563 )
1564 1564 continue
1565 1565
1566 1566 # abs: hgsep
1567 1567 # rel: ossep
1568 1568 srcs.append((abs, rel, exact))
1569 1569 return srcs
1570 1570
1571 1571 if ctx.rev() is not None:
1572 1572 rewriteutil.precheck(repo, [ctx.rev()], b'uncopy')
1573 1573 absdest = pathutil.canonpath(repo.root, cwd, dest)
1574 1574 if ctx.hasdir(absdest):
1575 1575 raise error.InputError(
1576 1576 _(b'%s: --at-rev does not support a directory as destination')
1577 1577 % uipathfn(absdest)
1578 1578 )
1579 1579 if absdest not in ctx:
1580 1580 raise error.InputError(
1581 1581 _(b'%s: copy destination does not exist in %s')
1582 1582 % (uipathfn(absdest), ctx)
1583 1583 )
1584 1584
1585 1585 # avoid cycle context -> subrepo -> cmdutil
1586 1586 from . import context
1587 1587
1588 1588 copylist = []
1589 1589 for pat in pats:
1590 1590 srcs = walkpat(pat)
1591 1591 if not srcs:
1592 1592 continue
1593 1593 for abs, rel, exact in srcs:
1594 1594 copylist.append(abs)
1595 1595
1596 1596 if not copylist:
1597 1597 raise error.InputError(_(b'no files to copy'))
1598 1598 # TODO: Add support for `hg cp --at-rev . foo bar dir` and
1599 1599 # `hg cp --at-rev . dir1 dir2`, preferably unifying the code with the
1600 1600 # existing functions below.
1601 1601 if len(copylist) != 1:
1602 1602 raise error.InputError(_(b'--at-rev requires a single source'))
1603 1603
1604 1604 new_ctx = context.overlayworkingctx(repo)
1605 1605 new_ctx.setbase(ctx.p1())
1606 1606 mergemod.graft(repo, ctx, wctx=new_ctx)
1607 1607
1608 1608 new_ctx.markcopied(absdest, copylist[0])
1609 1609
1610 1610 with repo.lock():
1611 1611 mem_ctx = new_ctx.tomemctx_for_amend(ctx)
1612 1612 new_node = mem_ctx.commit()
1613 1613
1614 1614 if repo.dirstate.p1() == ctx.node():
1615 1615 with repo.dirstate.parentchange():
1616 1616 scmutil.movedirstate(repo, repo[new_node])
1617 1617 replacements = {ctx.node(): [new_node]}
1618 1618 scmutil.cleanupnodes(repo, replacements, b'copy', fixphase=True)
1619 1619
1620 1620 return
1621 1621
1622 1622 # abssrc: hgsep
1623 1623 # relsrc: ossep
1624 1624 # otarget: ossep
1625 1625 def copyfile(abssrc, relsrc, otarget, exact):
1626 1626 abstarget = pathutil.canonpath(repo.root, cwd, otarget)
1627 1627 if b'/' in abstarget:
1628 1628 # We cannot normalize abstarget itself, this would prevent
1629 1629 # case only renames, like a => A.
1630 1630 abspath, absname = abstarget.rsplit(b'/', 1)
1631 1631 abstarget = repo.dirstate.normalize(abspath) + b'/' + absname
1632 1632 reltarget = repo.pathto(abstarget, cwd)
1633 1633 target = repo.wjoin(abstarget)
1634 1634 src = repo.wjoin(abssrc)
1635 1635 state = repo.dirstate[abstarget]
1636 1636
1637 1637 scmutil.checkportable(ui, abstarget)
1638 1638
1639 1639 # check for collisions
1640 1640 prevsrc = targets.get(abstarget)
1641 1641 if prevsrc is not None:
1642 1642 ui.warn(
1643 1643 _(b'%s: not overwriting - %s collides with %s\n')
1644 1644 % (
1645 1645 reltarget,
1646 1646 repo.pathto(abssrc, cwd),
1647 1647 repo.pathto(prevsrc, cwd),
1648 1648 )
1649 1649 )
1650 1650 return True # report a failure
1651 1651
1652 1652 # check for overwrites
1653 1653 exists = os.path.lexists(target)
1654 1654 samefile = False
1655 1655 if exists and abssrc != abstarget:
1656 1656 if repo.dirstate.normalize(abssrc) == repo.dirstate.normalize(
1657 1657 abstarget
1658 1658 ):
1659 1659 if not rename:
1660 1660 ui.warn(_(b"%s: can't copy - same file\n") % reltarget)
1661 1661 return True # report a failure
1662 1662 exists = False
1663 1663 samefile = True
1664 1664
1665 1665 if not after and exists or after and state in b'mn':
1666 1666 if not opts[b'force']:
1667 1667 if state in b'mn':
1668 1668 msg = _(b'%s: not overwriting - file already committed\n')
1669 1669 if after:
1670 1670 flags = b'--after --force'
1671 1671 else:
1672 1672 flags = b'--force'
1673 1673 if rename:
1674 1674 hint = (
1675 1675 _(
1676 1676 b"('hg rename %s' to replace the file by "
1677 1677 b'recording a rename)\n'
1678 1678 )
1679 1679 % flags
1680 1680 )
1681 1681 else:
1682 1682 hint = (
1683 1683 _(
1684 1684 b"('hg copy %s' to replace the file by "
1685 1685 b'recording a copy)\n'
1686 1686 )
1687 1687 % flags
1688 1688 )
1689 1689 else:
1690 1690 msg = _(b'%s: not overwriting - file exists\n')
1691 1691 if rename:
1692 1692 hint = _(
1693 1693 b"('hg rename --after' to record the rename)\n"
1694 1694 )
1695 1695 else:
1696 1696 hint = _(b"('hg copy --after' to record the copy)\n")
1697 1697 ui.warn(msg % reltarget)
1698 1698 ui.warn(hint)
1699 1699 return True # report a failure
1700 1700
1701 1701 if after:
1702 1702 if not exists:
1703 1703 if rename:
1704 1704 ui.warn(
1705 1705 _(b'%s: not recording move - %s does not exist\n')
1706 1706 % (relsrc, reltarget)
1707 1707 )
1708 1708 else:
1709 1709 ui.warn(
1710 1710 _(b'%s: not recording copy - %s does not exist\n')
1711 1711 % (relsrc, reltarget)
1712 1712 )
1713 1713 return True # report a failure
1714 1714 elif not dryrun:
1715 1715 try:
1716 1716 if exists:
1717 1717 os.unlink(target)
1718 1718 targetdir = os.path.dirname(target) or b'.'
1719 1719 if not os.path.isdir(targetdir):
1720 1720 os.makedirs(targetdir)
1721 1721 if samefile:
1722 1722 tmp = target + b"~hgrename"
1723 1723 os.rename(src, tmp)
1724 1724 os.rename(tmp, target)
1725 1725 else:
1726 1726 # Preserve stat info on renames, not on copies; this matches
1727 1727 # Linux CLI behavior.
1728 1728 util.copyfile(src, target, copystat=rename)
1729 1729 srcexists = True
1730 1730 except IOError as inst:
1731 1731 if inst.errno == errno.ENOENT:
1732 1732 ui.warn(_(b'%s: deleted in working directory\n') % relsrc)
1733 1733 srcexists = False
1734 1734 else:
1735 1735 ui.warn(
1736 1736 _(b'%s: cannot copy - %s\n')
1737 1737 % (relsrc, encoding.strtolocal(inst.strerror))
1738 1738 )
1739 1739 return True # report a failure
1740 1740
1741 1741 if ui.verbose or not exact:
1742 1742 if rename:
1743 1743 ui.status(_(b'moving %s to %s\n') % (relsrc, reltarget))
1744 1744 else:
1745 1745 ui.status(_(b'copying %s to %s\n') % (relsrc, reltarget))
1746 1746
1747 1747 targets[abstarget] = abssrc
1748 1748
1749 1749 # fix up dirstate
1750 1750 scmutil.dirstatecopy(
1751 1751 ui, repo, ctx, abssrc, abstarget, dryrun=dryrun, cwd=cwd
1752 1752 )
1753 1753 if rename and not dryrun:
1754 1754 if not after and srcexists and not samefile:
1755 1755 rmdir = repo.ui.configbool(b'experimental', b'removeemptydirs')
1756 1756 repo.wvfs.unlinkpath(abssrc, rmdir=rmdir)
1757 1757 ctx.forget([abssrc])
1758 1758
1759 1759 # pat: ossep
1760 1760 # dest ossep
1761 1761 # srcs: list of (hgsep, hgsep, ossep, bool)
1762 1762 # return: function that takes hgsep and returns ossep
1763 1763 def targetpathfn(pat, dest, srcs):
1764 1764 if os.path.isdir(pat):
1765 1765 abspfx = pathutil.canonpath(repo.root, cwd, pat)
1766 1766 abspfx = util.localpath(abspfx)
1767 1767 if destdirexists:
1768 1768 striplen = len(os.path.split(abspfx)[0])
1769 1769 else:
1770 1770 striplen = len(abspfx)
1771 1771 if striplen:
1772 1772 striplen += len(pycompat.ossep)
1773 1773 res = lambda p: os.path.join(dest, util.localpath(p)[striplen:])
1774 1774 elif destdirexists:
1775 1775 res = lambda p: os.path.join(
1776 1776 dest, os.path.basename(util.localpath(p))
1777 1777 )
1778 1778 else:
1779 1779 res = lambda p: dest
1780 1780 return res
1781 1781
1782 1782 # pat: ossep
1783 1783 # dest ossep
1784 1784 # srcs: list of (hgsep, hgsep, ossep, bool)
1785 1785 # return: function that takes hgsep and returns ossep
1786 1786 def targetpathafterfn(pat, dest, srcs):
1787 1787 if matchmod.patkind(pat):
1788 1788 # a mercurial pattern
1789 1789 res = lambda p: os.path.join(
1790 1790 dest, os.path.basename(util.localpath(p))
1791 1791 )
1792 1792 else:
1793 1793 abspfx = pathutil.canonpath(repo.root, cwd, pat)
1794 1794 if len(abspfx) < len(srcs[0][0]):
1795 1795 # A directory. Either the target path contains the last
1796 1796 # component of the source path or it does not.
1797 1797 def evalpath(striplen):
1798 1798 score = 0
1799 1799 for s in srcs:
1800 1800 t = os.path.join(dest, util.localpath(s[0])[striplen:])
1801 1801 if os.path.lexists(t):
1802 1802 score += 1
1803 1803 return score
1804 1804
1805 1805 abspfx = util.localpath(abspfx)
1806 1806 striplen = len(abspfx)
1807 1807 if striplen:
1808 1808 striplen += len(pycompat.ossep)
1809 1809 if os.path.isdir(os.path.join(dest, os.path.split(abspfx)[1])):
1810 1810 score = evalpath(striplen)
1811 1811 striplen1 = len(os.path.split(abspfx)[0])
1812 1812 if striplen1:
1813 1813 striplen1 += len(pycompat.ossep)
1814 1814 if evalpath(striplen1) > score:
1815 1815 striplen = striplen1
1816 1816 res = lambda p: os.path.join(dest, util.localpath(p)[striplen:])
1817 1817 else:
1818 1818 # a file
1819 1819 if destdirexists:
1820 1820 res = lambda p: os.path.join(
1821 1821 dest, os.path.basename(util.localpath(p))
1822 1822 )
1823 1823 else:
1824 1824 res = lambda p: dest
1825 1825 return res
1826 1826
1827 1827 destdirexists = os.path.isdir(dest) and not os.path.islink(dest)
1828 1828 if not destdirexists:
1829 1829 if len(pats) > 1 or matchmod.patkind(pats[0]):
1830 1830 raise error.InputError(
1831 1831 _(
1832 1832 b'with multiple sources, destination must be an '
1833 1833 b'existing directory'
1834 1834 )
1835 1835 )
1836 1836 if util.endswithsep(dest):
1837 1837 raise error.InputError(
1838 1838 _(b'destination %s is not a directory') % dest
1839 1839 )
1840 1840
1841 1841 tfn = targetpathfn
1842 1842 if after:
1843 1843 tfn = targetpathafterfn
1844 1844 copylist = []
1845 1845 for pat in pats:
1846 1846 srcs = walkpat(pat)
1847 1847 if not srcs:
1848 1848 continue
1849 1849 copylist.append((tfn(pat, dest, srcs), srcs))
1850 1850 if not copylist:
1851 1851 raise error.InputError(_(b'no files to copy'))
1852 1852
1853 1853 errors = 0
1854 1854 for targetpath, srcs in copylist:
1855 1855 for abssrc, relsrc, exact in srcs:
1856 1856 if copyfile(abssrc, relsrc, targetpath(abssrc), exact):
1857 1857 errors += 1
1858 1858
1859 1859 return errors != 0
1860 1860
1861 1861
1862 1862 ## facility to let extension process additional data into an import patch
1863 1863 # list of identifier to be executed in order
1864 1864 extrapreimport = [] # run before commit
1865 1865 extrapostimport = [] # run after commit
1866 1866 # mapping from identifier to actual import function
1867 1867 #
1868 1868 # 'preimport' are run before the commit is made and are provided the following
1869 1869 # arguments:
1870 1870 # - repo: the localrepository instance,
1871 1871 # - patchdata: data extracted from patch header (cf m.patch.patchheadermap),
1872 1872 # - extra: the future extra dictionary of the changeset, please mutate it,
1873 1873 # - opts: the import options.
1874 1874 # XXX ideally, we would just pass an ctx ready to be computed, that would allow
1875 1875 # mutation of in memory commit and more. Feel free to rework the code to get
1876 1876 # there.
1877 1877 extrapreimportmap = {}
1878 1878 # 'postimport' are run after the commit is made and are provided the following
1879 1879 # argument:
1880 1880 # - ctx: the changectx created by import.
1881 1881 extrapostimportmap = {}
1882 1882
1883 1883
1884 1884 def tryimportone(ui, repo, patchdata, parents, opts, msgs, updatefunc):
1885 1885 """Utility function used by commands.import to import a single patch
1886 1886
1887 1887 This function is explicitly defined here to help the evolve extension to
1888 1888 wrap this part of the import logic.
1889 1889
1890 1890 The API is currently a bit ugly because it a simple code translation from
1891 1891 the import command. Feel free to make it better.
1892 1892
1893 1893 :patchdata: a dictionary containing parsed patch data (such as from
1894 1894 ``patch.extract()``)
1895 1895 :parents: nodes that will be parent of the created commit
1896 1896 :opts: the full dict of option passed to the import command
1897 1897 :msgs: list to save commit message to.
1898 1898 (used in case we need to save it when failing)
1899 1899 :updatefunc: a function that update a repo to a given node
1900 1900 updatefunc(<repo>, <node>)
1901 1901 """
1902 1902 # avoid cycle context -> subrepo -> cmdutil
1903 1903 from . import context
1904 1904
1905 1905 tmpname = patchdata.get(b'filename')
1906 1906 message = patchdata.get(b'message')
1907 1907 user = opts.get(b'user') or patchdata.get(b'user')
1908 1908 date = opts.get(b'date') or patchdata.get(b'date')
1909 1909 branch = patchdata.get(b'branch')
1910 1910 nodeid = patchdata.get(b'nodeid')
1911 1911 p1 = patchdata.get(b'p1')
1912 1912 p2 = patchdata.get(b'p2')
1913 1913
1914 1914 nocommit = opts.get(b'no_commit')
1915 1915 importbranch = opts.get(b'import_branch')
1916 1916 update = not opts.get(b'bypass')
1917 1917 strip = opts[b"strip"]
1918 1918 prefix = opts[b"prefix"]
1919 1919 sim = float(opts.get(b'similarity') or 0)
1920 1920
1921 1921 if not tmpname:
1922 1922 return None, None, False
1923 1923
1924 1924 rejects = False
1925 1925
1926 1926 cmdline_message = logmessage(ui, opts)
1927 1927 if cmdline_message:
1928 1928 # pickup the cmdline msg
1929 1929 message = cmdline_message
1930 1930 elif message:
1931 1931 # pickup the patch msg
1932 1932 message = message.strip()
1933 1933 else:
1934 1934 # launch the editor
1935 1935 message = None
1936 1936 ui.debug(b'message:\n%s\n' % (message or b''))
1937 1937
1938 1938 if len(parents) == 1:
1939 1939 parents.append(repo[nullid])
1940 1940 if opts.get(b'exact'):
1941 1941 if not nodeid or not p1:
1942 1942 raise error.InputError(_(b'not a Mercurial patch'))
1943 1943 p1 = repo[p1]
1944 1944 p2 = repo[p2 or nullid]
1945 1945 elif p2:
1946 1946 try:
1947 1947 p1 = repo[p1]
1948 1948 p2 = repo[p2]
1949 1949 # Without any options, consider p2 only if the
1950 1950 # patch is being applied on top of the recorded
1951 1951 # first parent.
1952 1952 if p1 != parents[0]:
1953 1953 p1 = parents[0]
1954 1954 p2 = repo[nullid]
1955 1955 except error.RepoError:
1956 1956 p1, p2 = parents
1957 1957 if p2.node() == nullid:
1958 1958 ui.warn(
1959 1959 _(
1960 1960 b"warning: import the patch as a normal revision\n"
1961 1961 b"(use --exact to import the patch as a merge)\n"
1962 1962 )
1963 1963 )
1964 1964 else:
1965 1965 p1, p2 = parents
1966 1966
1967 1967 n = None
1968 1968 if update:
1969 1969 if p1 != parents[0]:
1970 1970 updatefunc(repo, p1.node())
1971 1971 if p2 != parents[1]:
1972 1972 repo.setparents(p1.node(), p2.node())
1973 1973
1974 1974 if opts.get(b'exact') or importbranch:
1975 1975 repo.dirstate.setbranch(branch or b'default')
1976 1976
1977 1977 partial = opts.get(b'partial', False)
1978 1978 files = set()
1979 1979 try:
1980 1980 patch.patch(
1981 1981 ui,
1982 1982 repo,
1983 1983 tmpname,
1984 1984 strip=strip,
1985 1985 prefix=prefix,
1986 1986 files=files,
1987 1987 eolmode=None,
1988 1988 similarity=sim / 100.0,
1989 1989 )
1990 1990 except error.PatchError as e:
1991 1991 if not partial:
1992 1992 raise error.Abort(pycompat.bytestr(e))
1993 1993 if partial:
1994 1994 rejects = True
1995 1995
1996 1996 files = list(files)
1997 1997 if nocommit:
1998 1998 if message:
1999 1999 msgs.append(message)
2000 2000 else:
2001 2001 if opts.get(b'exact') or p2:
2002 2002 # If you got here, you either use --force and know what
2003 2003 # you are doing or used --exact or a merge patch while
2004 2004 # being updated to its first parent.
2005 2005 m = None
2006 2006 else:
2007 2007 m = scmutil.matchfiles(repo, files or [])
2008 2008 editform = mergeeditform(repo[None], b'import.normal')
2009 2009 if opts.get(b'exact'):
2010 2010 editor = None
2011 2011 else:
2012 2012 editor = getcommiteditor(
2013 2013 editform=editform, **pycompat.strkwargs(opts)
2014 2014 )
2015 2015 extra = {}
2016 2016 for idfunc in extrapreimport:
2017 2017 extrapreimportmap[idfunc](repo, patchdata, extra, opts)
2018 2018 overrides = {}
2019 2019 if partial:
2020 2020 overrides[(b'ui', b'allowemptycommit')] = True
2021 2021 if opts.get(b'secret'):
2022 2022 overrides[(b'phases', b'new-commit')] = b'secret'
2023 2023 with repo.ui.configoverride(overrides, b'import'):
2024 2024 n = repo.commit(
2025 2025 message, user, date, match=m, editor=editor, extra=extra
2026 2026 )
2027 2027 for idfunc in extrapostimport:
2028 2028 extrapostimportmap[idfunc](repo[n])
2029 2029 else:
2030 2030 if opts.get(b'exact') or importbranch:
2031 2031 branch = branch or b'default'
2032 2032 else:
2033 2033 branch = p1.branch()
2034 2034 store = patch.filestore()
2035 2035 try:
2036 2036 files = set()
2037 2037 try:
2038 2038 patch.patchrepo(
2039 2039 ui,
2040 2040 repo,
2041 2041 p1,
2042 2042 store,
2043 2043 tmpname,
2044 2044 strip,
2045 2045 prefix,
2046 2046 files,
2047 2047 eolmode=None,
2048 2048 )
2049 2049 except error.PatchError as e:
2050 2050 raise error.Abort(stringutil.forcebytestr(e))
2051 2051 if opts.get(b'exact'):
2052 2052 editor = None
2053 2053 else:
2054 2054 editor = getcommiteditor(editform=b'import.bypass')
2055 2055 memctx = context.memctx(
2056 2056 repo,
2057 2057 (p1.node(), p2.node()),
2058 2058 message,
2059 2059 files=files,
2060 2060 filectxfn=store,
2061 2061 user=user,
2062 2062 date=date,
2063 2063 branch=branch,
2064 2064 editor=editor,
2065 2065 )
2066 2066
2067 2067 overrides = {}
2068 2068 if opts.get(b'secret'):
2069 2069 overrides[(b'phases', b'new-commit')] = b'secret'
2070 2070 with repo.ui.configoverride(overrides, b'import'):
2071 2071 n = memctx.commit()
2072 2072 finally:
2073 2073 store.close()
2074 2074 if opts.get(b'exact') and nocommit:
2075 2075 # --exact with --no-commit is still useful in that it does merge
2076 2076 # and branch bits
2077 2077 ui.warn(_(b"warning: can't check exact import with --no-commit\n"))
2078 2078 elif opts.get(b'exact') and (not n or hex(n) != nodeid):
2079 2079 raise error.Abort(_(b'patch is damaged or loses information'))
2080 2080 msg = _(b'applied to working directory')
2081 2081 if n:
2082 2082 # i18n: refers to a short changeset id
2083 2083 msg = _(b'created %s') % short(n)
2084 2084 return msg, n, rejects
2085 2085
2086 2086
2087 2087 # facility to let extensions include additional data in an exported patch
2088 2088 # list of identifiers to be executed in order
2089 2089 extraexport = []
2090 2090 # mapping from identifier to actual export function
2091 2091 # function as to return a string to be added to the header or None
2092 2092 # it is given two arguments (sequencenumber, changectx)
2093 2093 extraexportmap = {}
2094 2094
2095 2095
2096 2096 def _exportsingle(repo, ctx, fm, match, switch_parent, seqno, diffopts):
2097 2097 node = scmutil.binnode(ctx)
2098 2098 parents = [p.node() for p in ctx.parents() if p]
2099 2099 branch = ctx.branch()
2100 2100 if switch_parent:
2101 2101 parents.reverse()
2102 2102
2103 2103 if parents:
2104 2104 prev = parents[0]
2105 2105 else:
2106 2106 prev = nullid
2107 2107
2108 2108 fm.context(ctx=ctx)
2109 2109 fm.plain(b'# HG changeset patch\n')
2110 2110 fm.write(b'user', b'# User %s\n', ctx.user())
2111 2111 fm.plain(b'# Date %d %d\n' % ctx.date())
2112 2112 fm.write(b'date', b'# %s\n', fm.formatdate(ctx.date()))
2113 2113 fm.condwrite(
2114 2114 branch and branch != b'default', b'branch', b'# Branch %s\n', branch
2115 2115 )
2116 2116 fm.write(b'node', b'# Node ID %s\n', hex(node))
2117 2117 fm.plain(b'# Parent %s\n' % hex(prev))
2118 2118 if len(parents) > 1:
2119 2119 fm.plain(b'# Parent %s\n' % hex(parents[1]))
2120 2120 fm.data(parents=fm.formatlist(pycompat.maplist(hex, parents), name=b'node'))
2121 2121
2122 2122 # TODO: redesign extraexportmap function to support formatter
2123 2123 for headerid in extraexport:
2124 2124 header = extraexportmap[headerid](seqno, ctx)
2125 2125 if header is not None:
2126 2126 fm.plain(b'# %s\n' % header)
2127 2127
2128 2128 fm.write(b'desc', b'%s\n', ctx.description().rstrip())
2129 2129 fm.plain(b'\n')
2130 2130
2131 2131 if fm.isplain():
2132 2132 chunkiter = patch.diffui(repo, prev, node, match, opts=diffopts)
2133 2133 for chunk, label in chunkiter:
2134 2134 fm.plain(chunk, label=label)
2135 2135 else:
2136 2136 chunkiter = patch.diff(repo, prev, node, match, opts=diffopts)
2137 2137 # TODO: make it structured?
2138 2138 fm.data(diff=b''.join(chunkiter))
2139 2139
2140 2140
2141 2141 def _exportfile(repo, revs, fm, dest, switch_parent, diffopts, match):
2142 2142 """Export changesets to stdout or a single file"""
2143 2143 for seqno, rev in enumerate(revs, 1):
2144 2144 ctx = repo[rev]
2145 2145 if not dest.startswith(b'<'):
2146 2146 repo.ui.note(b"%s\n" % dest)
2147 2147 fm.startitem()
2148 2148 _exportsingle(repo, ctx, fm, match, switch_parent, seqno, diffopts)
2149 2149
2150 2150
2151 2151 def _exportfntemplate(
2152 2152 repo, revs, basefm, fntemplate, switch_parent, diffopts, match
2153 2153 ):
2154 2154 """Export changesets to possibly multiple files"""
2155 2155 total = len(revs)
2156 2156 revwidth = max(len(str(rev)) for rev in revs)
2157 2157 filemap = util.sortdict() # filename: [(seqno, rev), ...]
2158 2158
2159 2159 for seqno, rev in enumerate(revs, 1):
2160 2160 ctx = repo[rev]
2161 2161 dest = makefilename(
2162 2162 ctx, fntemplate, total=total, seqno=seqno, revwidth=revwidth
2163 2163 )
2164 2164 filemap.setdefault(dest, []).append((seqno, rev))
2165 2165
2166 2166 for dest in filemap:
2167 2167 with formatter.maybereopen(basefm, dest) as fm:
2168 2168 repo.ui.note(b"%s\n" % dest)
2169 2169 for seqno, rev in filemap[dest]:
2170 2170 fm.startitem()
2171 2171 ctx = repo[rev]
2172 2172 _exportsingle(
2173 2173 repo, ctx, fm, match, switch_parent, seqno, diffopts
2174 2174 )
2175 2175
2176 2176
2177 2177 def _prefetchchangedfiles(repo, revs, match):
2178 2178 allfiles = set()
2179 2179 for rev in revs:
2180 2180 for file in repo[rev].files():
2181 2181 if not match or match(file):
2182 2182 allfiles.add(file)
2183 2183 match = scmutil.matchfiles(repo, allfiles)
2184 2184 revmatches = [(rev, match) for rev in revs]
2185 2185 scmutil.prefetchfiles(repo, revmatches)
2186 2186
2187 2187
2188 2188 def export(
2189 2189 repo,
2190 2190 revs,
2191 2191 basefm,
2192 2192 fntemplate=b'hg-%h.patch',
2193 2193 switch_parent=False,
2194 2194 opts=None,
2195 2195 match=None,
2196 2196 ):
2197 2197 """export changesets as hg patches
2198 2198
2199 2199 Args:
2200 2200 repo: The repository from which we're exporting revisions.
2201 2201 revs: A list of revisions to export as revision numbers.
2202 2202 basefm: A formatter to which patches should be written.
2203 2203 fntemplate: An optional string to use for generating patch file names.
2204 2204 switch_parent: If True, show diffs against second parent when not nullid.
2205 2205 Default is false, which always shows diff against p1.
2206 2206 opts: diff options to use for generating the patch.
2207 2207 match: If specified, only export changes to files matching this matcher.
2208 2208
2209 2209 Returns:
2210 2210 Nothing.
2211 2211
2212 2212 Side Effect:
2213 2213 "HG Changeset Patch" data is emitted to one of the following
2214 2214 destinations:
2215 2215 fntemplate specified: Each rev is written to a unique file named using
2216 2216 the given template.
2217 2217 Otherwise: All revs will be written to basefm.
2218 2218 """
2219 2219 _prefetchchangedfiles(repo, revs, match)
2220 2220
2221 2221 if not fntemplate:
2222 2222 _exportfile(
2223 2223 repo, revs, basefm, b'<unnamed>', switch_parent, opts, match
2224 2224 )
2225 2225 else:
2226 2226 _exportfntemplate(
2227 2227 repo, revs, basefm, fntemplate, switch_parent, opts, match
2228 2228 )
2229 2229
2230 2230
2231 2231 def exportfile(repo, revs, fp, switch_parent=False, opts=None, match=None):
2232 2232 """Export changesets to the given file stream"""
2233 2233 _prefetchchangedfiles(repo, revs, match)
2234 2234
2235 2235 dest = getattr(fp, 'name', b'<unnamed>')
2236 2236 with formatter.formatter(repo.ui, fp, b'export', {}) as fm:
2237 2237 _exportfile(repo, revs, fm, dest, switch_parent, opts, match)
2238 2238
2239 2239
2240 2240 def showmarker(fm, marker, index=None):
2241 2241 """utility function to display obsolescence marker in a readable way
2242 2242
2243 2243 To be used by debug function."""
2244 2244 if index is not None:
2245 2245 fm.write(b'index', b'%i ', index)
2246 2246 fm.write(b'prednode', b'%s ', hex(marker.prednode()))
2247 2247 succs = marker.succnodes()
2248 2248 fm.condwrite(
2249 2249 succs,
2250 2250 b'succnodes',
2251 2251 b'%s ',
2252 2252 fm.formatlist(map(hex, succs), name=b'node'),
2253 2253 )
2254 2254 fm.write(b'flag', b'%X ', marker.flags())
2255 2255 parents = marker.parentnodes()
2256 2256 if parents is not None:
2257 2257 fm.write(
2258 2258 b'parentnodes',
2259 2259 b'{%s} ',
2260 2260 fm.formatlist(map(hex, parents), name=b'node', sep=b', '),
2261 2261 )
2262 2262 fm.write(b'date', b'(%s) ', fm.formatdate(marker.date()))
2263 2263 meta = marker.metadata().copy()
2264 2264 meta.pop(b'date', None)
2265 2265 smeta = pycompat.rapply(pycompat.maybebytestr, meta)
2266 2266 fm.write(
2267 2267 b'metadata', b'{%s}', fm.formatdict(smeta, fmt=b'%r: %r', sep=b', ')
2268 2268 )
2269 2269 fm.plain(b'\n')
2270 2270
2271 2271
2272 2272 def finddate(ui, repo, date):
2273 2273 """Find the tipmost changeset that matches the given date spec"""
2274 2274 mrevs = repo.revs(b'date(%s)', date)
2275 2275 try:
2276 2276 rev = mrevs.max()
2277 2277 except ValueError:
2278 2278 raise error.InputError(_(b"revision matching date not found"))
2279 2279
2280 2280 ui.status(
2281 2281 _(b"found revision %d from %s\n")
2282 2282 % (rev, dateutil.datestr(repo[rev].date()))
2283 2283 )
2284 2284 return b'%d' % rev
2285 2285
2286 2286
2287 2287 def add(ui, repo, match, prefix, uipathfn, explicitonly, **opts):
2288 2288 bad = []
2289 2289
2290 2290 badfn = lambda x, y: bad.append(x) or match.bad(x, y)
2291 2291 names = []
2292 2292 wctx = repo[None]
2293 2293 cca = None
2294 2294 abort, warn = scmutil.checkportabilityalert(ui)
2295 2295 if abort or warn:
2296 2296 cca = scmutil.casecollisionauditor(ui, abort, repo.dirstate)
2297 2297
2298 2298 match = repo.narrowmatch(match, includeexact=True)
2299 2299 badmatch = matchmod.badmatch(match, badfn)
2300 2300 dirstate = repo.dirstate
2301 2301 # We don't want to just call wctx.walk here, since it would return a lot of
2302 2302 # clean files, which we aren't interested in and takes time.
2303 2303 for f in sorted(
2304 2304 dirstate.walk(
2305 2305 badmatch,
2306 2306 subrepos=sorted(wctx.substate),
2307 2307 unknown=True,
2308 2308 ignored=False,
2309 2309 full=False,
2310 2310 )
2311 2311 ):
2312 2312 exact = match.exact(f)
2313 2313 if exact or not explicitonly and f not in wctx and repo.wvfs.lexists(f):
2314 2314 if cca:
2315 2315 cca(f)
2316 2316 names.append(f)
2317 2317 if ui.verbose or not exact:
2318 2318 ui.status(
2319 2319 _(b'adding %s\n') % uipathfn(f), label=b'ui.addremove.added'
2320 2320 )
2321 2321
2322 2322 for subpath in sorted(wctx.substate):
2323 2323 sub = wctx.sub(subpath)
2324 2324 try:
2325 2325 submatch = matchmod.subdirmatcher(subpath, match)
2326 2326 subprefix = repo.wvfs.reljoin(prefix, subpath)
2327 2327 subuipathfn = scmutil.subdiruipathfn(subpath, uipathfn)
2328 2328 if opts.get('subrepos'):
2329 2329 bad.extend(
2330 2330 sub.add(ui, submatch, subprefix, subuipathfn, False, **opts)
2331 2331 )
2332 2332 else:
2333 2333 bad.extend(
2334 2334 sub.add(ui, submatch, subprefix, subuipathfn, True, **opts)
2335 2335 )
2336 2336 except error.LookupError:
2337 2337 ui.status(
2338 2338 _(b"skipping missing subrepository: %s\n") % uipathfn(subpath)
2339 2339 )
2340 2340
2341 2341 if not opts.get('dry_run'):
2342 2342 rejected = wctx.add(names, prefix)
2343 2343 bad.extend(f for f in rejected if f in match.files())
2344 2344 return bad
2345 2345
2346 2346
2347 2347 def addwebdirpath(repo, serverpath, webconf):
2348 2348 webconf[serverpath] = repo.root
2349 2349 repo.ui.debug(b'adding %s = %s\n' % (serverpath, repo.root))
2350 2350
2351 2351 for r in repo.revs(b'filelog("path:.hgsub")'):
2352 2352 ctx = repo[r]
2353 2353 for subpath in ctx.substate:
2354 2354 ctx.sub(subpath).addwebdirpath(serverpath, webconf)
2355 2355
2356 2356
2357 2357 def forget(
2358 2358 ui, repo, match, prefix, uipathfn, explicitonly, dryrun, interactive
2359 2359 ):
2360 2360 if dryrun and interactive:
2361 2361 raise error.InputError(
2362 2362 _(b"cannot specify both --dry-run and --interactive")
2363 2363 )
2364 2364 bad = []
2365 2365 badfn = lambda x, y: bad.append(x) or match.bad(x, y)
2366 2366 wctx = repo[None]
2367 2367 forgot = []
2368 2368
2369 2369 s = repo.status(match=matchmod.badmatch(match, badfn), clean=True)
2370 2370 forget = sorted(s.modified + s.added + s.deleted + s.clean)
2371 2371 if explicitonly:
2372 2372 forget = [f for f in forget if match.exact(f)]
2373 2373
2374 2374 for subpath in sorted(wctx.substate):
2375 2375 sub = wctx.sub(subpath)
2376 2376 submatch = matchmod.subdirmatcher(subpath, match)
2377 2377 subprefix = repo.wvfs.reljoin(prefix, subpath)
2378 2378 subuipathfn = scmutil.subdiruipathfn(subpath, uipathfn)
2379 2379 try:
2380 2380 subbad, subforgot = sub.forget(
2381 2381 submatch,
2382 2382 subprefix,
2383 2383 subuipathfn,
2384 2384 dryrun=dryrun,
2385 2385 interactive=interactive,
2386 2386 )
2387 2387 bad.extend([subpath + b'/' + f for f in subbad])
2388 2388 forgot.extend([subpath + b'/' + f for f in subforgot])
2389 2389 except error.LookupError:
2390 2390 ui.status(
2391 2391 _(b"skipping missing subrepository: %s\n") % uipathfn(subpath)
2392 2392 )
2393 2393
2394 2394 if not explicitonly:
2395 2395 for f in match.files():
2396 2396 if f not in repo.dirstate and not repo.wvfs.isdir(f):
2397 2397 if f not in forgot:
2398 2398 if repo.wvfs.exists(f):
2399 2399 # Don't complain if the exact case match wasn't given.
2400 2400 # But don't do this until after checking 'forgot', so
2401 2401 # that subrepo files aren't normalized, and this op is
2402 2402 # purely from data cached by the status walk above.
2403 2403 if repo.dirstate.normalize(f) in repo.dirstate:
2404 2404 continue
2405 2405 ui.warn(
2406 2406 _(
2407 2407 b'not removing %s: '
2408 2408 b'file is already untracked\n'
2409 2409 )
2410 2410 % uipathfn(f)
2411 2411 )
2412 2412 bad.append(f)
2413 2413
2414 2414 if interactive:
2415 2415 responses = _(
2416 2416 b'[Ynsa?]'
2417 2417 b'$$ &Yes, forget this file'
2418 2418 b'$$ &No, skip this file'
2419 2419 b'$$ &Skip remaining files'
2420 2420 b'$$ Include &all remaining files'
2421 2421 b'$$ &? (display help)'
2422 2422 )
2423 2423 for filename in forget[:]:
2424 2424 r = ui.promptchoice(
2425 2425 _(b'forget %s %s') % (uipathfn(filename), responses)
2426 2426 )
2427 2427 if r == 4: # ?
2428 2428 while r == 4:
2429 2429 for c, t in ui.extractchoices(responses)[1]:
2430 2430 ui.write(b'%s - %s\n' % (c, encoding.lower(t)))
2431 2431 r = ui.promptchoice(
2432 2432 _(b'forget %s %s') % (uipathfn(filename), responses)
2433 2433 )
2434 2434 if r == 0: # yes
2435 2435 continue
2436 2436 elif r == 1: # no
2437 2437 forget.remove(filename)
2438 2438 elif r == 2: # Skip
2439 2439 fnindex = forget.index(filename)
2440 2440 del forget[fnindex:]
2441 2441 break
2442 2442 elif r == 3: # All
2443 2443 break
2444 2444
2445 2445 for f in forget:
2446 2446 if ui.verbose or not match.exact(f) or interactive:
2447 2447 ui.status(
2448 2448 _(b'removing %s\n') % uipathfn(f), label=b'ui.addremove.removed'
2449 2449 )
2450 2450
2451 2451 if not dryrun:
2452 2452 rejected = wctx.forget(forget, prefix)
2453 2453 bad.extend(f for f in rejected if f in match.files())
2454 2454 forgot.extend(f for f in forget if f not in rejected)
2455 2455 return bad, forgot
2456 2456
2457 2457
2458 2458 def files(ui, ctx, m, uipathfn, fm, fmt, subrepos):
2459 2459 ret = 1
2460 2460
2461 2461 needsfctx = ui.verbose or {b'size', b'flags'} & fm.datahint()
2462 2462 if fm.isplain() and not needsfctx:
2463 2463 # Fast path. The speed-up comes from skipping the formatter, and batching
2464 2464 # calls to ui.write.
2465 2465 buf = []
2466 2466 for f in ctx.matches(m):
2467 2467 buf.append(fmt % uipathfn(f))
2468 2468 if len(buf) > 100:
2469 2469 ui.write(b''.join(buf))
2470 2470 del buf[:]
2471 2471 ret = 0
2472 2472 if buf:
2473 2473 ui.write(b''.join(buf))
2474 2474 else:
2475 2475 for f in ctx.matches(m):
2476 2476 fm.startitem()
2477 2477 fm.context(ctx=ctx)
2478 2478 if needsfctx:
2479 2479 fc = ctx[f]
2480 2480 fm.write(b'size flags', b'% 10d % 1s ', fc.size(), fc.flags())
2481 2481 fm.data(path=f)
2482 2482 fm.plain(fmt % uipathfn(f))
2483 2483 ret = 0
2484 2484
2485 2485 for subpath in sorted(ctx.substate):
2486 2486 submatch = matchmod.subdirmatcher(subpath, m)
2487 2487 subuipathfn = scmutil.subdiruipathfn(subpath, uipathfn)
2488 2488 if subrepos or m.exact(subpath) or any(submatch.files()):
2489 2489 sub = ctx.sub(subpath)
2490 2490 try:
2491 2491 recurse = m.exact(subpath) or subrepos
2492 2492 if (
2493 2493 sub.printfiles(ui, submatch, subuipathfn, fm, fmt, recurse)
2494 2494 == 0
2495 2495 ):
2496 2496 ret = 0
2497 2497 except error.LookupError:
2498 2498 ui.status(
2499 2499 _(b"skipping missing subrepository: %s\n")
2500 2500 % uipathfn(subpath)
2501 2501 )
2502 2502
2503 2503 return ret
2504 2504
2505 2505
2506 2506 def remove(
2507 2507 ui, repo, m, prefix, uipathfn, after, force, subrepos, dryrun, warnings=None
2508 2508 ):
2509 2509 ret = 0
2510 2510 s = repo.status(match=m, clean=True)
2511 2511 modified, added, deleted, clean = s.modified, s.added, s.deleted, s.clean
2512 2512
2513 2513 wctx = repo[None]
2514 2514
2515 2515 if warnings is None:
2516 2516 warnings = []
2517 2517 warn = True
2518 2518 else:
2519 2519 warn = False
2520 2520
2521 2521 subs = sorted(wctx.substate)
2522 2522 progress = ui.makeprogress(
2523 2523 _(b'searching'), total=len(subs), unit=_(b'subrepos')
2524 2524 )
2525 2525 for subpath in subs:
2526 2526 submatch = matchmod.subdirmatcher(subpath, m)
2527 2527 subprefix = repo.wvfs.reljoin(prefix, subpath)
2528 2528 subuipathfn = scmutil.subdiruipathfn(subpath, uipathfn)
2529 2529 if subrepos or m.exact(subpath) or any(submatch.files()):
2530 2530 progress.increment()
2531 2531 sub = wctx.sub(subpath)
2532 2532 try:
2533 2533 if sub.removefiles(
2534 2534 submatch,
2535 2535 subprefix,
2536 2536 subuipathfn,
2537 2537 after,
2538 2538 force,
2539 2539 subrepos,
2540 2540 dryrun,
2541 2541 warnings,
2542 2542 ):
2543 2543 ret = 1
2544 2544 except error.LookupError:
2545 2545 warnings.append(
2546 2546 _(b"skipping missing subrepository: %s\n")
2547 2547 % uipathfn(subpath)
2548 2548 )
2549 2549 progress.complete()
2550 2550
2551 2551 # warn about failure to delete explicit files/dirs
2552 2552 deleteddirs = pathutil.dirs(deleted)
2553 2553 files = m.files()
2554 2554 progress = ui.makeprogress(
2555 2555 _(b'deleting'), total=len(files), unit=_(b'files')
2556 2556 )
2557 2557 for f in files:
2558 2558
2559 2559 def insubrepo():
2560 2560 for subpath in wctx.substate:
2561 2561 if f.startswith(subpath + b'/'):
2562 2562 return True
2563 2563 return False
2564 2564
2565 2565 progress.increment()
2566 2566 isdir = f in deleteddirs or wctx.hasdir(f)
2567 2567 if f in repo.dirstate or isdir or f == b'.' or insubrepo() or f in subs:
2568 2568 continue
2569 2569
2570 2570 if repo.wvfs.exists(f):
2571 2571 if repo.wvfs.isdir(f):
2572 2572 warnings.append(
2573 2573 _(b'not removing %s: no tracked files\n') % uipathfn(f)
2574 2574 )
2575 2575 else:
2576 2576 warnings.append(
2577 2577 _(b'not removing %s: file is untracked\n') % uipathfn(f)
2578 2578 )
2579 2579 # missing files will generate a warning elsewhere
2580 2580 ret = 1
2581 2581 progress.complete()
2582 2582
2583 2583 if force:
2584 2584 list = modified + deleted + clean + added
2585 2585 elif after:
2586 2586 list = deleted
2587 2587 remaining = modified + added + clean
2588 2588 progress = ui.makeprogress(
2589 2589 _(b'skipping'), total=len(remaining), unit=_(b'files')
2590 2590 )
2591 2591 for f in remaining:
2592 2592 progress.increment()
2593 2593 if ui.verbose or (f in files):
2594 2594 warnings.append(
2595 2595 _(b'not removing %s: file still exists\n') % uipathfn(f)
2596 2596 )
2597 2597 ret = 1
2598 2598 progress.complete()
2599 2599 else:
2600 2600 list = deleted + clean
2601 2601 progress = ui.makeprogress(
2602 2602 _(b'skipping'), total=(len(modified) + len(added)), unit=_(b'files')
2603 2603 )
2604 2604 for f in modified:
2605 2605 progress.increment()
2606 2606 warnings.append(
2607 2607 _(
2608 2608 b'not removing %s: file is modified (use -f'
2609 2609 b' to force removal)\n'
2610 2610 )
2611 2611 % uipathfn(f)
2612 2612 )
2613 2613 ret = 1
2614 2614 for f in added:
2615 2615 progress.increment()
2616 2616 warnings.append(
2617 2617 _(
2618 2618 b"not removing %s: file has been marked for add"
2619 2619 b" (use 'hg forget' to undo add)\n"
2620 2620 )
2621 2621 % uipathfn(f)
2622 2622 )
2623 2623 ret = 1
2624 2624 progress.complete()
2625 2625
2626 2626 list = sorted(list)
2627 2627 progress = ui.makeprogress(
2628 2628 _(b'deleting'), total=len(list), unit=_(b'files')
2629 2629 )
2630 2630 for f in list:
2631 2631 if ui.verbose or not m.exact(f):
2632 2632 progress.increment()
2633 2633 ui.status(
2634 2634 _(b'removing %s\n') % uipathfn(f), label=b'ui.addremove.removed'
2635 2635 )
2636 2636 progress.complete()
2637 2637
2638 2638 if not dryrun:
2639 2639 with repo.wlock():
2640 2640 if not after:
2641 2641 for f in list:
2642 2642 if f in added:
2643 2643 continue # we never unlink added files on remove
2644 2644 rmdir = repo.ui.configbool(
2645 2645 b'experimental', b'removeemptydirs'
2646 2646 )
2647 2647 repo.wvfs.unlinkpath(f, ignoremissing=True, rmdir=rmdir)
2648 2648 repo[None].forget(list)
2649 2649
2650 2650 if warn:
2651 2651 for warning in warnings:
2652 2652 ui.warn(warning)
2653 2653
2654 2654 return ret
2655 2655
2656 2656
2657 2657 def _catfmtneedsdata(fm):
2658 2658 return not fm.datahint() or b'data' in fm.datahint()
2659 2659
2660 2660
2661 2661 def _updatecatformatter(fm, ctx, matcher, path, decode):
2662 2662 """Hook for adding data to the formatter used by ``hg cat``.
2663 2663
2664 2664 Extensions (e.g., lfs) can wrap this to inject keywords/data, but must call
2665 2665 this method first."""
2666 2666
2667 2667 # data() can be expensive to fetch (e.g. lfs), so don't fetch it if it
2668 2668 # wasn't requested.
2669 2669 data = b''
2670 2670 if _catfmtneedsdata(fm):
2671 2671 data = ctx[path].data()
2672 2672 if decode:
2673 2673 data = ctx.repo().wwritedata(path, data)
2674 2674 fm.startitem()
2675 2675 fm.context(ctx=ctx)
2676 2676 fm.write(b'data', b'%s', data)
2677 2677 fm.data(path=path)
2678 2678
2679 2679
2680 2680 def cat(ui, repo, ctx, matcher, basefm, fntemplate, prefix, **opts):
2681 2681 err = 1
2682 2682 opts = pycompat.byteskwargs(opts)
2683 2683
2684 2684 def write(path):
2685 2685 filename = None
2686 2686 if fntemplate:
2687 2687 filename = makefilename(
2688 2688 ctx, fntemplate, pathname=os.path.join(prefix, path)
2689 2689 )
2690 2690 # attempt to create the directory if it does not already exist
2691 2691 try:
2692 2692 os.makedirs(os.path.dirname(filename))
2693 2693 except OSError:
2694 2694 pass
2695 2695 with formatter.maybereopen(basefm, filename) as fm:
2696 2696 _updatecatformatter(fm, ctx, matcher, path, opts.get(b'decode'))
2697 2697
2698 2698 # Automation often uses hg cat on single files, so special case it
2699 2699 # for performance to avoid the cost of parsing the manifest.
2700 2700 if len(matcher.files()) == 1 and not matcher.anypats():
2701 2701 file = matcher.files()[0]
2702 2702 mfl = repo.manifestlog
2703 2703 mfnode = ctx.manifestnode()
2704 2704 try:
2705 2705 if mfnode and mfl[mfnode].find(file)[0]:
2706 2706 if _catfmtneedsdata(basefm):
2707 2707 scmutil.prefetchfiles(repo, [(ctx.rev(), matcher)])
2708 2708 write(file)
2709 2709 return 0
2710 2710 except KeyError:
2711 2711 pass
2712 2712
2713 2713 if _catfmtneedsdata(basefm):
2714 2714 scmutil.prefetchfiles(repo, [(ctx.rev(), matcher)])
2715 2715
2716 2716 for abs in ctx.walk(matcher):
2717 2717 write(abs)
2718 2718 err = 0
2719 2719
2720 2720 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
2721 2721 for subpath in sorted(ctx.substate):
2722 2722 sub = ctx.sub(subpath)
2723 2723 try:
2724 2724 submatch = matchmod.subdirmatcher(subpath, matcher)
2725 2725 subprefix = os.path.join(prefix, subpath)
2726 2726 if not sub.cat(
2727 2727 submatch,
2728 2728 basefm,
2729 2729 fntemplate,
2730 2730 subprefix,
2731 2731 **pycompat.strkwargs(opts)
2732 2732 ):
2733 2733 err = 0
2734 2734 except error.RepoLookupError:
2735 2735 ui.status(
2736 2736 _(b"skipping missing subrepository: %s\n") % uipathfn(subpath)
2737 2737 )
2738 2738
2739 2739 return err
2740 2740
2741 2741
2742 2742 def commit(ui, repo, commitfunc, pats, opts):
2743 2743 '''commit the specified files or all outstanding changes'''
2744 2744 date = opts.get(b'date')
2745 2745 if date:
2746 2746 opts[b'date'] = dateutil.parsedate(date)
2747 2747 message = logmessage(ui, opts)
2748 2748 matcher = scmutil.match(repo[None], pats, opts)
2749 2749
2750 2750 dsguard = None
2751 2751 # extract addremove carefully -- this function can be called from a command
2752 2752 # that doesn't support addremove
2753 2753 if opts.get(b'addremove'):
2754 2754 dsguard = dirstateguard.dirstateguard(repo, b'commit')
2755 2755 with dsguard or util.nullcontextmanager():
2756 2756 if dsguard:
2757 2757 relative = scmutil.anypats(pats, opts)
2758 2758 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=relative)
2759 2759 if scmutil.addremove(repo, matcher, b"", uipathfn, opts) != 0:
2760 2760 raise error.Abort(
2761 2761 _(b"failed to mark all new/missing files as added/removed")
2762 2762 )
2763 2763
2764 2764 return commitfunc(ui, repo, message, matcher, opts)
2765 2765
2766 2766
2767 2767 def samefile(f, ctx1, ctx2):
2768 2768 if f in ctx1.manifest():
2769 2769 a = ctx1.filectx(f)
2770 2770 if f in ctx2.manifest():
2771 2771 b = ctx2.filectx(f)
2772 2772 return not a.cmp(b) and a.flags() == b.flags()
2773 2773 else:
2774 2774 return False
2775 2775 else:
2776 2776 return f not in ctx2.manifest()
2777 2777
2778 2778
2779 2779 def amend(ui, repo, old, extra, pats, opts):
2780 2780 # avoid cycle context -> subrepo -> cmdutil
2781 2781 from . import context
2782 2782
2783 2783 # amend will reuse the existing user if not specified, but the obsolete
2784 2784 # marker creation requires that the current user's name is specified.
2785 2785 if obsolete.isenabled(repo, obsolete.createmarkersopt):
2786 2786 ui.username() # raise exception if username not set
2787 2787
2788 2788 ui.note(_(b'amending changeset %s\n') % old)
2789 2789 base = old.p1()
2790 2790
2791 2791 with repo.wlock(), repo.lock(), repo.transaction(b'amend'):
2792 2792 # Participating changesets:
2793 2793 #
2794 2794 # wctx o - workingctx that contains changes from working copy
2795 2795 # | to go into amending commit
2796 2796 # |
2797 2797 # old o - changeset to amend
2798 2798 # |
2799 2799 # base o - first parent of the changeset to amend
2800 2800 wctx = repo[None]
2801 2801
2802 2802 # Copy to avoid mutating input
2803 2803 extra = extra.copy()
2804 2804 # Update extra dict from amended commit (e.g. to preserve graft
2805 2805 # source)
2806 2806 extra.update(old.extra())
2807 2807
2808 2808 # Also update it from the from the wctx
2809 2809 extra.update(wctx.extra())
2810 2810
2811 2811 # date-only change should be ignored?
2812 2812 datemaydiffer = resolvecommitoptions(ui, opts)
2813 2813
2814 2814 date = old.date()
2815 2815 if opts.get(b'date'):
2816 2816 date = dateutil.parsedate(opts.get(b'date'))
2817 2817 user = opts.get(b'user') or old.user()
2818 2818
2819 2819 if len(old.parents()) > 1:
2820 2820 # ctx.files() isn't reliable for merges, so fall back to the
2821 2821 # slower repo.status() method
2822 2822 st = base.status(old)
2823 2823 files = set(st.modified) | set(st.added) | set(st.removed)
2824 2824 else:
2825 2825 files = set(old.files())
2826 2826
2827 2827 # add/remove the files to the working copy if the "addremove" option
2828 2828 # was specified.
2829 2829 matcher = scmutil.match(wctx, pats, opts)
2830 2830 relative = scmutil.anypats(pats, opts)
2831 2831 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=relative)
2832 2832 if opts.get(b'addremove') and scmutil.addremove(
2833 2833 repo, matcher, b"", uipathfn, opts
2834 2834 ):
2835 2835 raise error.Abort(
2836 2836 _(b"failed to mark all new/missing files as added/removed")
2837 2837 )
2838 2838
2839 2839 # Check subrepos. This depends on in-place wctx._status update in
2840 2840 # subrepo.precommit(). To minimize the risk of this hack, we do
2841 2841 # nothing if .hgsub does not exist.
2842 2842 if b'.hgsub' in wctx or b'.hgsub' in old:
2843 2843 subs, commitsubs, newsubstate = subrepoutil.precommit(
2844 2844 ui, wctx, wctx._status, matcher
2845 2845 )
2846 2846 # amend should abort if commitsubrepos is enabled
2847 2847 assert not commitsubs
2848 2848 if subs:
2849 2849 subrepoutil.writestate(repo, newsubstate)
2850 2850
2851 2851 ms = mergestatemod.mergestate.read(repo)
2852 2852 mergeutil.checkunresolved(ms)
2853 2853
2854 2854 filestoamend = {f for f in wctx.files() if matcher(f)}
2855 2855
2856 2856 changes = len(filestoamend) > 0
2857 2857 if changes:
2858 2858 # Recompute copies (avoid recording a -> b -> a)
2859 2859 copied = copies.pathcopies(base, wctx, matcher)
2860 2860 if old.p2:
2861 2861 copied.update(copies.pathcopies(old.p2(), wctx, matcher))
2862 2862
2863 2863 # Prune files which were reverted by the updates: if old
2864 2864 # introduced file X and the file was renamed in the working
2865 2865 # copy, then those two files are the same and
2866 2866 # we can discard X from our list of files. Likewise if X
2867 2867 # was removed, it's no longer relevant. If X is missing (aka
2868 2868 # deleted), old X must be preserved.
2869 2869 files.update(filestoamend)
2870 2870 files = [
2871 2871 f
2872 2872 for f in files
2873 2873 if (f not in filestoamend or not samefile(f, wctx, base))
2874 2874 ]
2875 2875
2876 2876 def filectxfn(repo, ctx_, path):
2877 2877 try:
2878 2878 # If the file being considered is not amongst the files
2879 2879 # to be amended, we should return the file context from the
2880 2880 # old changeset. This avoids issues when only some files in
2881 2881 # the working copy are being amended but there are also
2882 2882 # changes to other files from the old changeset.
2883 2883 if path not in filestoamend:
2884 2884 return old.filectx(path)
2885 2885
2886 2886 # Return None for removed files.
2887 2887 if path in wctx.removed():
2888 2888 return None
2889 2889
2890 2890 fctx = wctx[path]
2891 2891 flags = fctx.flags()
2892 2892 mctx = context.memfilectx(
2893 2893 repo,
2894 2894 ctx_,
2895 2895 fctx.path(),
2896 2896 fctx.data(),
2897 2897 islink=b'l' in flags,
2898 2898 isexec=b'x' in flags,
2899 2899 copysource=copied.get(path),
2900 2900 )
2901 2901 return mctx
2902 2902 except KeyError:
2903 2903 return None
2904 2904
2905 2905 else:
2906 2906 ui.note(_(b'copying changeset %s to %s\n') % (old, base))
2907 2907
2908 2908 # Use version of files as in the old cset
2909 2909 def filectxfn(repo, ctx_, path):
2910 2910 try:
2911 2911 return old.filectx(path)
2912 2912 except KeyError:
2913 2913 return None
2914 2914
2915 2915 # See if we got a message from -m or -l, if not, open the editor with
2916 2916 # the message of the changeset to amend.
2917 2917 message = logmessage(ui, opts)
2918 2918
2919 2919 editform = mergeeditform(old, b'commit.amend')
2920 2920
2921 2921 if not message:
2922 2922 message = old.description()
2923 2923 # Default if message isn't provided and --edit is not passed is to
2924 2924 # invoke editor, but allow --no-edit. If somehow we don't have any
2925 2925 # description, let's always start the editor.
2926 2926 doedit = not message or opts.get(b'edit') in [True, None]
2927 2927 else:
2928 2928 # Default if message is provided is to not invoke editor, but allow
2929 2929 # --edit.
2930 2930 doedit = opts.get(b'edit') is True
2931 2931 editor = getcommiteditor(edit=doedit, editform=editform)
2932 2932
2933 2933 pureextra = extra.copy()
2934 2934 extra[b'amend_source'] = old.hex()
2935 2935
2936 2936 new = context.memctx(
2937 2937 repo,
2938 2938 parents=[base.node(), old.p2().node()],
2939 2939 text=message,
2940 2940 files=files,
2941 2941 filectxfn=filectxfn,
2942 2942 user=user,
2943 2943 date=date,
2944 2944 extra=extra,
2945 2945 editor=editor,
2946 2946 )
2947 2947
2948 2948 newdesc = changelog.stripdesc(new.description())
2949 2949 if (
2950 2950 (not changes)
2951 2951 and newdesc == old.description()
2952 2952 and user == old.user()
2953 2953 and (date == old.date() or datemaydiffer)
2954 2954 and pureextra == old.extra()
2955 2955 ):
2956 2956 # nothing changed. continuing here would create a new node
2957 2957 # anyway because of the amend_source noise.
2958 2958 #
2959 2959 # This not what we expect from amend.
2960 2960 return old.node()
2961 2961
2962 2962 commitphase = None
2963 2963 if opts.get(b'secret'):
2964 2964 commitphase = phases.secret
2965 2965 newid = repo.commitctx(new)
2966 2966 ms.reset()
2967 2967
2968 2968 # Reroute the working copy parent to the new changeset
2969 2969 repo.setparents(newid, nullid)
2970 2970 mapping = {old.node(): (newid,)}
2971 2971 obsmetadata = None
2972 2972 if opts.get(b'note'):
2973 2973 obsmetadata = {b'note': encoding.fromlocal(opts[b'note'])}
2974 2974 backup = ui.configbool(b'rewrite', b'backup-bundle')
2975 2975 scmutil.cleanupnodes(
2976 2976 repo,
2977 2977 mapping,
2978 2978 b'amend',
2979 2979 metadata=obsmetadata,
2980 2980 fixphase=True,
2981 2981 targetphase=commitphase,
2982 2982 backup=backup,
2983 2983 )
2984 2984
2985 2985 # Fixing the dirstate because localrepo.commitctx does not update
2986 2986 # it. This is rather convenient because we did not need to update
2987 2987 # the dirstate for all the files in the new commit which commitctx
2988 2988 # could have done if it updated the dirstate. Now, we can
2989 2989 # selectively update the dirstate only for the amended files.
2990 2990 dirstate = repo.dirstate
2991 2991
2992 2992 # Update the state of the files which were added and modified in the
2993 2993 # amend to "normal" in the dirstate. We need to use "normallookup" since
2994 2994 # the files may have changed since the command started; using "normal"
2995 2995 # would mark them as clean but with uncommitted contents.
2996 2996 normalfiles = set(wctx.modified() + wctx.added()) & filestoamend
2997 2997 for f in normalfiles:
2998 2998 dirstate.normallookup(f)
2999 2999
3000 3000 # Update the state of files which were removed in the amend
3001 3001 # to "removed" in the dirstate.
3002 3002 removedfiles = set(wctx.removed()) & filestoamend
3003 3003 for f in removedfiles:
3004 3004 dirstate.drop(f)
3005 3005
3006 3006 return newid
3007 3007
3008 3008
3009 3009 def commiteditor(repo, ctx, subs, editform=b''):
3010 3010 if ctx.description():
3011 3011 return ctx.description()
3012 3012 return commitforceeditor(
3013 3013 repo, ctx, subs, editform=editform, unchangedmessagedetection=True
3014 3014 )
3015 3015
3016 3016
3017 3017 def commitforceeditor(
3018 3018 repo,
3019 3019 ctx,
3020 3020 subs,
3021 3021 finishdesc=None,
3022 3022 extramsg=None,
3023 3023 editform=b'',
3024 3024 unchangedmessagedetection=False,
3025 3025 ):
3026 3026 if not extramsg:
3027 3027 extramsg = _(b"Leave message empty to abort commit.")
3028 3028
3029 3029 forms = [e for e in editform.split(b'.') if e]
3030 3030 forms.insert(0, b'changeset')
3031 3031 templatetext = None
3032 3032 while forms:
3033 3033 ref = b'.'.join(forms)
3034 3034 if repo.ui.config(b'committemplate', ref):
3035 3035 templatetext = committext = buildcommittemplate(
3036 3036 repo, ctx, subs, extramsg, ref
3037 3037 )
3038 3038 break
3039 3039 forms.pop()
3040 3040 else:
3041 3041 committext = buildcommittext(repo, ctx, subs, extramsg)
3042 3042
3043 3043 # run editor in the repository root
3044 3044 olddir = encoding.getcwd()
3045 3045 os.chdir(repo.root)
3046 3046
3047 3047 # make in-memory changes visible to external process
3048 3048 tr = repo.currenttransaction()
3049 3049 repo.dirstate.write(tr)
3050 3050 pending = tr and tr.writepending() and repo.root
3051 3051
3052 3052 editortext = repo.ui.edit(
3053 3053 committext,
3054 3054 ctx.user(),
3055 3055 ctx.extra(),
3056 3056 editform=editform,
3057 3057 pending=pending,
3058 3058 repopath=repo.path,
3059 3059 action=b'commit',
3060 3060 )
3061 3061 text = editortext
3062 3062
3063 3063 # strip away anything below this special string (used for editors that want
3064 3064 # to display the diff)
3065 3065 stripbelow = re.search(_linebelow, text, flags=re.MULTILINE)
3066 3066 if stripbelow:
3067 3067 text = text[: stripbelow.start()]
3068 3068
3069 3069 text = re.sub(b"(?m)^HG:.*(\n|$)", b"", text)
3070 3070 os.chdir(olddir)
3071 3071
3072 3072 if finishdesc:
3073 3073 text = finishdesc(text)
3074 3074 if not text.strip():
3075 3075 raise error.InputError(_(b"empty commit message"))
3076 3076 if unchangedmessagedetection and editortext == templatetext:
3077 3077 raise error.InputError(_(b"commit message unchanged"))
3078 3078
3079 3079 return text
3080 3080
3081 3081
3082 3082 def buildcommittemplate(repo, ctx, subs, extramsg, ref):
3083 3083 ui = repo.ui
3084 3084 spec = formatter.reference_templatespec(ref)
3085 3085 t = logcmdutil.changesettemplater(ui, repo, spec)
3086 3086 t.t.cache.update(
3087 3087 (k, templater.unquotestring(v))
3088 3088 for k, v in repo.ui.configitems(b'committemplate')
3089 3089 )
3090 3090
3091 3091 if not extramsg:
3092 3092 extramsg = b'' # ensure that extramsg is string
3093 3093
3094 3094 ui.pushbuffer()
3095 3095 t.show(ctx, extramsg=extramsg)
3096 3096 return ui.popbuffer()
3097 3097
3098 3098
3099 3099 def hgprefix(msg):
3100 3100 return b"\n".join([b"HG: %s" % a for a in msg.split(b"\n") if a])
3101 3101
3102 3102
3103 3103 def buildcommittext(repo, ctx, subs, extramsg):
3104 3104 edittext = []
3105 3105 modified, added, removed = ctx.modified(), ctx.added(), ctx.removed()
3106 3106 if ctx.description():
3107 3107 edittext.append(ctx.description())
3108 3108 edittext.append(b"")
3109 3109 edittext.append(b"") # Empty line between message and comments.
3110 3110 edittext.append(
3111 3111 hgprefix(
3112 3112 _(
3113 3113 b"Enter commit message."
3114 3114 b" Lines beginning with 'HG:' are removed."
3115 3115 )
3116 3116 )
3117 3117 )
3118 3118 edittext.append(hgprefix(extramsg))
3119 3119 edittext.append(b"HG: --")
3120 3120 edittext.append(hgprefix(_(b"user: %s") % ctx.user()))
3121 3121 if ctx.p2():
3122 3122 edittext.append(hgprefix(_(b"branch merge")))
3123 3123 if ctx.branch():
3124 3124 edittext.append(hgprefix(_(b"branch '%s'") % ctx.branch()))
3125 3125 if bookmarks.isactivewdirparent(repo):
3126 3126 edittext.append(hgprefix(_(b"bookmark '%s'") % repo._activebookmark))
3127 3127 edittext.extend([hgprefix(_(b"subrepo %s") % s) for s in subs])
3128 3128 edittext.extend([hgprefix(_(b"added %s") % f) for f in added])
3129 3129 edittext.extend([hgprefix(_(b"changed %s") % f) for f in modified])
3130 3130 edittext.extend([hgprefix(_(b"removed %s") % f) for f in removed])
3131 3131 if not added and not modified and not removed:
3132 3132 edittext.append(hgprefix(_(b"no files changed")))
3133 3133 edittext.append(b"")
3134 3134
3135 3135 return b"\n".join(edittext)
3136 3136
3137 3137
3138 3138 def commitstatus(repo, node, branch, bheads=None, tip=None, opts=None):
3139 3139 if opts is None:
3140 3140 opts = {}
3141 3141 ctx = repo[node]
3142 3142 parents = ctx.parents()
3143 3143
3144 3144 if tip is not None and repo.changelog.tip() == tip:
3145 3145 # avoid reporting something like "committed new head" when
3146 3146 # recommitting old changesets, and issue a helpful warning
3147 3147 # for most instances
3148 repo.ui.warn(_("warning: commit already existed in the repository!\n"))
3148 repo.ui.warn(_(b"warning: commit already existed in the repository!\n"))
3149 3149 elif (
3150 3150 not opts.get(b'amend')
3151 3151 and bheads
3152 3152 and node not in bheads
3153 3153 and not any(
3154 3154 p.node() in bheads and p.branch() == branch for p in parents
3155 3155 )
3156 3156 ):
3157 3157 repo.ui.status(_(b'created new head\n'))
3158 3158 # The message is not printed for initial roots. For the other
3159 3159 # changesets, it is printed in the following situations:
3160 3160 #
3161 3161 # Par column: for the 2 parents with ...
3162 3162 # N: null or no parent
3163 3163 # B: parent is on another named branch
3164 3164 # C: parent is a regular non head changeset
3165 3165 # H: parent was a branch head of the current branch
3166 3166 # Msg column: whether we print "created new head" message
3167 3167 # In the following, it is assumed that there already exists some
3168 3168 # initial branch heads of the current branch, otherwise nothing is
3169 3169 # printed anyway.
3170 3170 #
3171 3171 # Par Msg Comment
3172 3172 # N N y additional topo root
3173 3173 #
3174 3174 # B N y additional branch root
3175 3175 # C N y additional topo head
3176 3176 # H N n usual case
3177 3177 #
3178 3178 # B B y weird additional branch root
3179 3179 # C B y branch merge
3180 3180 # H B n merge with named branch
3181 3181 #
3182 3182 # C C y additional head from merge
3183 3183 # C H n merge with a head
3184 3184 #
3185 3185 # H H n head merge: head count decreases
3186 3186
3187 3187 if not opts.get(b'close_branch'):
3188 3188 for r in parents:
3189 3189 if r.closesbranch() and r.branch() == branch:
3190 3190 repo.ui.status(
3191 3191 _(b'reopening closed branch head %d\n') % r.rev()
3192 3192 )
3193 3193
3194 3194 if repo.ui.debugflag:
3195 3195 repo.ui.write(
3196 3196 _(b'committed changeset %d:%s\n') % (ctx.rev(), ctx.hex())
3197 3197 )
3198 3198 elif repo.ui.verbose:
3199 3199 repo.ui.write(_(b'committed changeset %d:%s\n') % (ctx.rev(), ctx))
3200 3200
3201 3201
3202 3202 def postcommitstatus(repo, pats, opts):
3203 3203 return repo.status(match=scmutil.match(repo[None], pats, opts))
3204 3204
3205 3205
3206 3206 def revert(ui, repo, ctx, *pats, **opts):
3207 3207 opts = pycompat.byteskwargs(opts)
3208 3208 parent, p2 = repo.dirstate.parents()
3209 3209 node = ctx.node()
3210 3210
3211 3211 mf = ctx.manifest()
3212 3212 if node == p2:
3213 3213 parent = p2
3214 3214
3215 3215 # need all matching names in dirstate and manifest of target rev,
3216 3216 # so have to walk both. do not print errors if files exist in one
3217 3217 # but not other. in both cases, filesets should be evaluated against
3218 3218 # workingctx to get consistent result (issue4497). this means 'set:**'
3219 3219 # cannot be used to select missing files from target rev.
3220 3220
3221 3221 # `names` is a mapping for all elements in working copy and target revision
3222 3222 # The mapping is in the form:
3223 3223 # <abs path in repo> -> (<path from CWD>, <exactly specified by matcher?>)
3224 3224 names = {}
3225 3225 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
3226 3226
3227 3227 with repo.wlock():
3228 3228 ## filling of the `names` mapping
3229 3229 # walk dirstate to fill `names`
3230 3230
3231 3231 interactive = opts.get(b'interactive', False)
3232 3232 wctx = repo[None]
3233 3233 m = scmutil.match(wctx, pats, opts)
3234 3234
3235 3235 # we'll need this later
3236 3236 targetsubs = sorted(s for s in wctx.substate if m(s))
3237 3237
3238 3238 if not m.always():
3239 3239 matcher = matchmod.badmatch(m, lambda x, y: False)
3240 3240 for abs in wctx.walk(matcher):
3241 3241 names[abs] = m.exact(abs)
3242 3242
3243 3243 # walk target manifest to fill `names`
3244 3244
3245 3245 def badfn(path, msg):
3246 3246 if path in names:
3247 3247 return
3248 3248 if path in ctx.substate:
3249 3249 return
3250 3250 path_ = path + b'/'
3251 3251 for f in names:
3252 3252 if f.startswith(path_):
3253 3253 return
3254 3254 ui.warn(b"%s: %s\n" % (uipathfn(path), msg))
3255 3255
3256 3256 for abs in ctx.walk(matchmod.badmatch(m, badfn)):
3257 3257 if abs not in names:
3258 3258 names[abs] = m.exact(abs)
3259 3259
3260 3260 # Find status of all file in `names`.
3261 3261 m = scmutil.matchfiles(repo, names)
3262 3262
3263 3263 changes = repo.status(
3264 3264 node1=node, match=m, unknown=True, ignored=True, clean=True
3265 3265 )
3266 3266 else:
3267 3267 changes = repo.status(node1=node, match=m)
3268 3268 for kind in changes:
3269 3269 for abs in kind:
3270 3270 names[abs] = m.exact(abs)
3271 3271
3272 3272 m = scmutil.matchfiles(repo, names)
3273 3273
3274 3274 modified = set(changes.modified)
3275 3275 added = set(changes.added)
3276 3276 removed = set(changes.removed)
3277 3277 _deleted = set(changes.deleted)
3278 3278 unknown = set(changes.unknown)
3279 3279 unknown.update(changes.ignored)
3280 3280 clean = set(changes.clean)
3281 3281 modadded = set()
3282 3282
3283 3283 # We need to account for the state of the file in the dirstate,
3284 3284 # even when we revert against something else than parent. This will
3285 3285 # slightly alter the behavior of revert (doing back up or not, delete
3286 3286 # or just forget etc).
3287 3287 if parent == node:
3288 3288 dsmodified = modified
3289 3289 dsadded = added
3290 3290 dsremoved = removed
3291 3291 # store all local modifications, useful later for rename detection
3292 3292 localchanges = dsmodified | dsadded
3293 3293 modified, added, removed = set(), set(), set()
3294 3294 else:
3295 3295 changes = repo.status(node1=parent, match=m)
3296 3296 dsmodified = set(changes.modified)
3297 3297 dsadded = set(changes.added)
3298 3298 dsremoved = set(changes.removed)
3299 3299 # store all local modifications, useful later for rename detection
3300 3300 localchanges = dsmodified | dsadded
3301 3301
3302 3302 # only take into account for removes between wc and target
3303 3303 clean |= dsremoved - removed
3304 3304 dsremoved &= removed
3305 3305 # distinct between dirstate remove and other
3306 3306 removed -= dsremoved
3307 3307
3308 3308 modadded = added & dsmodified
3309 3309 added -= modadded
3310 3310
3311 3311 # tell newly modified apart.
3312 3312 dsmodified &= modified
3313 3313 dsmodified |= modified & dsadded # dirstate added may need backup
3314 3314 modified -= dsmodified
3315 3315
3316 3316 # We need to wait for some post-processing to update this set
3317 3317 # before making the distinction. The dirstate will be used for
3318 3318 # that purpose.
3319 3319 dsadded = added
3320 3320
3321 3321 # in case of merge, files that are actually added can be reported as
3322 3322 # modified, we need to post process the result
3323 3323 if p2 != nullid:
3324 3324 mergeadd = set(dsmodified)
3325 3325 for path in dsmodified:
3326 3326 if path in mf:
3327 3327 mergeadd.remove(path)
3328 3328 dsadded |= mergeadd
3329 3329 dsmodified -= mergeadd
3330 3330
3331 3331 # if f is a rename, update `names` to also revert the source
3332 3332 for f in localchanges:
3333 3333 src = repo.dirstate.copied(f)
3334 3334 # XXX should we check for rename down to target node?
3335 3335 if src and src not in names and repo.dirstate[src] == b'r':
3336 3336 dsremoved.add(src)
3337 3337 names[src] = True
3338 3338
3339 3339 # determine the exact nature of the deleted changesets
3340 3340 deladded = set(_deleted)
3341 3341 for path in _deleted:
3342 3342 if path in mf:
3343 3343 deladded.remove(path)
3344 3344 deleted = _deleted - deladded
3345 3345
3346 3346 # distinguish between file to forget and the other
3347 3347 added = set()
3348 3348 for abs in dsadded:
3349 3349 if repo.dirstate[abs] != b'a':
3350 3350 added.add(abs)
3351 3351 dsadded -= added
3352 3352
3353 3353 for abs in deladded:
3354 3354 if repo.dirstate[abs] == b'a':
3355 3355 dsadded.add(abs)
3356 3356 deladded -= dsadded
3357 3357
3358 3358 # For files marked as removed, we check if an unknown file is present at
3359 3359 # the same path. If a such file exists it may need to be backed up.
3360 3360 # Making the distinction at this stage helps have simpler backup
3361 3361 # logic.
3362 3362 removunk = set()
3363 3363 for abs in removed:
3364 3364 target = repo.wjoin(abs)
3365 3365 if os.path.lexists(target):
3366 3366 removunk.add(abs)
3367 3367 removed -= removunk
3368 3368
3369 3369 dsremovunk = set()
3370 3370 for abs in dsremoved:
3371 3371 target = repo.wjoin(abs)
3372 3372 if os.path.lexists(target):
3373 3373 dsremovunk.add(abs)
3374 3374 dsremoved -= dsremovunk
3375 3375
3376 3376 # action to be actually performed by revert
3377 3377 # (<list of file>, message>) tuple
3378 3378 actions = {
3379 3379 b'revert': ([], _(b'reverting %s\n')),
3380 3380 b'add': ([], _(b'adding %s\n')),
3381 3381 b'remove': ([], _(b'removing %s\n')),
3382 3382 b'drop': ([], _(b'removing %s\n')),
3383 3383 b'forget': ([], _(b'forgetting %s\n')),
3384 3384 b'undelete': ([], _(b'undeleting %s\n')),
3385 3385 b'noop': (None, _(b'no changes needed to %s\n')),
3386 3386 b'unknown': (None, _(b'file not managed: %s\n')),
3387 3387 }
3388 3388
3389 3389 # "constant" that convey the backup strategy.
3390 3390 # All set to `discard` if `no-backup` is set do avoid checking
3391 3391 # no_backup lower in the code.
3392 3392 # These values are ordered for comparison purposes
3393 3393 backupinteractive = 3 # do backup if interactively modified
3394 3394 backup = 2 # unconditionally do backup
3395 3395 check = 1 # check if the existing file differs from target
3396 3396 discard = 0 # never do backup
3397 3397 if opts.get(b'no_backup'):
3398 3398 backupinteractive = backup = check = discard
3399 3399 if interactive:
3400 3400 dsmodifiedbackup = backupinteractive
3401 3401 else:
3402 3402 dsmodifiedbackup = backup
3403 3403 tobackup = set()
3404 3404
3405 3405 backupanddel = actions[b'remove']
3406 3406 if not opts.get(b'no_backup'):
3407 3407 backupanddel = actions[b'drop']
3408 3408
3409 3409 disptable = (
3410 3410 # dispatch table:
3411 3411 # file state
3412 3412 # action
3413 3413 # make backup
3414 3414 ## Sets that results that will change file on disk
3415 3415 # Modified compared to target, no local change
3416 3416 (modified, actions[b'revert'], discard),
3417 3417 # Modified compared to target, but local file is deleted
3418 3418 (deleted, actions[b'revert'], discard),
3419 3419 # Modified compared to target, local change
3420 3420 (dsmodified, actions[b'revert'], dsmodifiedbackup),
3421 3421 # Added since target
3422 3422 (added, actions[b'remove'], discard),
3423 3423 # Added in working directory
3424 3424 (dsadded, actions[b'forget'], discard),
3425 3425 # Added since target, have local modification
3426 3426 (modadded, backupanddel, backup),
3427 3427 # Added since target but file is missing in working directory
3428 3428 (deladded, actions[b'drop'], discard),
3429 3429 # Removed since target, before working copy parent
3430 3430 (removed, actions[b'add'], discard),
3431 3431 # Same as `removed` but an unknown file exists at the same path
3432 3432 (removunk, actions[b'add'], check),
3433 3433 # Removed since targe, marked as such in working copy parent
3434 3434 (dsremoved, actions[b'undelete'], discard),
3435 3435 # Same as `dsremoved` but an unknown file exists at the same path
3436 3436 (dsremovunk, actions[b'undelete'], check),
3437 3437 ## the following sets does not result in any file changes
3438 3438 # File with no modification
3439 3439 (clean, actions[b'noop'], discard),
3440 3440 # Existing file, not tracked anywhere
3441 3441 (unknown, actions[b'unknown'], discard),
3442 3442 )
3443 3443
3444 3444 for abs, exact in sorted(names.items()):
3445 3445 # target file to be touch on disk (relative to cwd)
3446 3446 target = repo.wjoin(abs)
3447 3447 # search the entry in the dispatch table.
3448 3448 # if the file is in any of these sets, it was touched in the working
3449 3449 # directory parent and we are sure it needs to be reverted.
3450 3450 for table, (xlist, msg), dobackup in disptable:
3451 3451 if abs not in table:
3452 3452 continue
3453 3453 if xlist is not None:
3454 3454 xlist.append(abs)
3455 3455 if dobackup:
3456 3456 # If in interactive mode, don't automatically create
3457 3457 # .orig files (issue4793)
3458 3458 if dobackup == backupinteractive:
3459 3459 tobackup.add(abs)
3460 3460 elif backup <= dobackup or wctx[abs].cmp(ctx[abs]):
3461 3461 absbakname = scmutil.backuppath(ui, repo, abs)
3462 3462 bakname = os.path.relpath(
3463 3463 absbakname, start=repo.root
3464 3464 )
3465 3465 ui.note(
3466 3466 _(b'saving current version of %s as %s\n')
3467 3467 % (uipathfn(abs), uipathfn(bakname))
3468 3468 )
3469 3469 if not opts.get(b'dry_run'):
3470 3470 if interactive:
3471 3471 util.copyfile(target, absbakname)
3472 3472 else:
3473 3473 util.rename(target, absbakname)
3474 3474 if opts.get(b'dry_run'):
3475 3475 if ui.verbose or not exact:
3476 3476 ui.status(msg % uipathfn(abs))
3477 3477 elif exact:
3478 3478 ui.warn(msg % uipathfn(abs))
3479 3479 break
3480 3480
3481 3481 if not opts.get(b'dry_run'):
3482 3482 needdata = (b'revert', b'add', b'undelete')
3483 3483 oplist = [actions[name][0] for name in needdata]
3484 3484 prefetch = scmutil.prefetchfiles
3485 3485 matchfiles = scmutil.matchfiles(
3486 3486 repo, [f for sublist in oplist for f in sublist]
3487 3487 )
3488 3488 prefetch(
3489 3489 repo,
3490 3490 [(ctx.rev(), matchfiles)],
3491 3491 )
3492 3492 match = scmutil.match(repo[None], pats)
3493 3493 _performrevert(
3494 3494 repo,
3495 3495 ctx,
3496 3496 names,
3497 3497 uipathfn,
3498 3498 actions,
3499 3499 match,
3500 3500 interactive,
3501 3501 tobackup,
3502 3502 )
3503 3503
3504 3504 if targetsubs:
3505 3505 # Revert the subrepos on the revert list
3506 3506 for sub in targetsubs:
3507 3507 try:
3508 3508 wctx.sub(sub).revert(
3509 3509 ctx.substate[sub], *pats, **pycompat.strkwargs(opts)
3510 3510 )
3511 3511 except KeyError:
3512 3512 raise error.Abort(
3513 3513 b"subrepository '%s' does not exist in %s!"
3514 3514 % (sub, short(ctx.node()))
3515 3515 )
3516 3516
3517 3517
3518 3518 def _performrevert(
3519 3519 repo,
3520 3520 ctx,
3521 3521 names,
3522 3522 uipathfn,
3523 3523 actions,
3524 3524 match,
3525 3525 interactive=False,
3526 3526 tobackup=None,
3527 3527 ):
3528 3528 """function that actually perform all the actions computed for revert
3529 3529
3530 3530 This is an independent function to let extension to plug in and react to
3531 3531 the imminent revert.
3532 3532
3533 3533 Make sure you have the working directory locked when calling this function.
3534 3534 """
3535 3535 parent, p2 = repo.dirstate.parents()
3536 3536 node = ctx.node()
3537 3537 excluded_files = []
3538 3538
3539 3539 def checkout(f):
3540 3540 fc = ctx[f]
3541 3541 repo.wwrite(f, fc.data(), fc.flags())
3542 3542
3543 3543 def doremove(f):
3544 3544 try:
3545 3545 rmdir = repo.ui.configbool(b'experimental', b'removeemptydirs')
3546 3546 repo.wvfs.unlinkpath(f, rmdir=rmdir)
3547 3547 except OSError:
3548 3548 pass
3549 3549 repo.dirstate.remove(f)
3550 3550
3551 3551 def prntstatusmsg(action, f):
3552 3552 exact = names[f]
3553 3553 if repo.ui.verbose or not exact:
3554 3554 repo.ui.status(actions[action][1] % uipathfn(f))
3555 3555
3556 3556 audit_path = pathutil.pathauditor(repo.root, cached=True)
3557 3557 for f in actions[b'forget'][0]:
3558 3558 if interactive:
3559 3559 choice = repo.ui.promptchoice(
3560 3560 _(b"forget added file %s (Yn)?$$ &Yes $$ &No") % uipathfn(f)
3561 3561 )
3562 3562 if choice == 0:
3563 3563 prntstatusmsg(b'forget', f)
3564 3564 repo.dirstate.drop(f)
3565 3565 else:
3566 3566 excluded_files.append(f)
3567 3567 else:
3568 3568 prntstatusmsg(b'forget', f)
3569 3569 repo.dirstate.drop(f)
3570 3570 for f in actions[b'remove'][0]:
3571 3571 audit_path(f)
3572 3572 if interactive:
3573 3573 choice = repo.ui.promptchoice(
3574 3574 _(b"remove added file %s (Yn)?$$ &Yes $$ &No") % uipathfn(f)
3575 3575 )
3576 3576 if choice == 0:
3577 3577 prntstatusmsg(b'remove', f)
3578 3578 doremove(f)
3579 3579 else:
3580 3580 excluded_files.append(f)
3581 3581 else:
3582 3582 prntstatusmsg(b'remove', f)
3583 3583 doremove(f)
3584 3584 for f in actions[b'drop'][0]:
3585 3585 audit_path(f)
3586 3586 prntstatusmsg(b'drop', f)
3587 3587 repo.dirstate.remove(f)
3588 3588
3589 3589 normal = None
3590 3590 if node == parent:
3591 3591 # We're reverting to our parent. If possible, we'd like status
3592 3592 # to report the file as clean. We have to use normallookup for
3593 3593 # merges to avoid losing information about merged/dirty files.
3594 3594 if p2 != nullid:
3595 3595 normal = repo.dirstate.normallookup
3596 3596 else:
3597 3597 normal = repo.dirstate.normal
3598 3598
3599 3599 newlyaddedandmodifiedfiles = set()
3600 3600 if interactive:
3601 3601 # Prompt the user for changes to revert
3602 3602 torevert = [f for f in actions[b'revert'][0] if f not in excluded_files]
3603 3603 m = scmutil.matchfiles(repo, torevert)
3604 3604 diffopts = patch.difffeatureopts(
3605 3605 repo.ui,
3606 3606 whitespace=True,
3607 3607 section=b'commands',
3608 3608 configprefix=b'revert.interactive.',
3609 3609 )
3610 3610 diffopts.nodates = True
3611 3611 diffopts.git = True
3612 3612 operation = b'apply'
3613 3613 if node == parent:
3614 3614 if repo.ui.configbool(
3615 3615 b'experimental', b'revert.interactive.select-to-keep'
3616 3616 ):
3617 3617 operation = b'keep'
3618 3618 else:
3619 3619 operation = b'discard'
3620 3620
3621 3621 if operation == b'apply':
3622 3622 diff = patch.diff(repo, None, ctx.node(), m, opts=diffopts)
3623 3623 else:
3624 3624 diff = patch.diff(repo, ctx.node(), None, m, opts=diffopts)
3625 3625 originalchunks = patch.parsepatch(diff)
3626 3626
3627 3627 try:
3628 3628
3629 3629 chunks, opts = recordfilter(
3630 3630 repo.ui, originalchunks, match, operation=operation
3631 3631 )
3632 3632 if operation == b'discard':
3633 3633 chunks = patch.reversehunks(chunks)
3634 3634
3635 3635 except error.PatchError as err:
3636 3636 raise error.Abort(_(b'error parsing patch: %s') % err)
3637 3637
3638 3638 # FIXME: when doing an interactive revert of a copy, there's no way of
3639 3639 # performing a partial revert of the added file, the only option is
3640 3640 # "remove added file <name> (Yn)?", so we don't need to worry about the
3641 3641 # alsorestore value. Ideally we'd be able to partially revert
3642 3642 # copied/renamed files.
3643 3643 newlyaddedandmodifiedfiles, unusedalsorestore = newandmodified(
3644 3644 chunks, originalchunks
3645 3645 )
3646 3646 if tobackup is None:
3647 3647 tobackup = set()
3648 3648 # Apply changes
3649 3649 fp = stringio()
3650 3650 # chunks are serialized per file, but files aren't sorted
3651 3651 for f in sorted({c.header.filename() for c in chunks if ishunk(c)}):
3652 3652 prntstatusmsg(b'revert', f)
3653 3653 files = set()
3654 3654 for c in chunks:
3655 3655 if ishunk(c):
3656 3656 abs = c.header.filename()
3657 3657 # Create a backup file only if this hunk should be backed up
3658 3658 if c.header.filename() in tobackup:
3659 3659 target = repo.wjoin(abs)
3660 3660 bakname = scmutil.backuppath(repo.ui, repo, abs)
3661 3661 util.copyfile(target, bakname)
3662 3662 tobackup.remove(abs)
3663 3663 if abs not in files:
3664 3664 files.add(abs)
3665 3665 if operation == b'keep':
3666 3666 checkout(abs)
3667 3667 c.write(fp)
3668 3668 dopatch = fp.tell()
3669 3669 fp.seek(0)
3670 3670 if dopatch:
3671 3671 try:
3672 3672 patch.internalpatch(repo.ui, repo, fp, 1, eolmode=None)
3673 3673 except error.PatchError as err:
3674 3674 raise error.Abort(pycompat.bytestr(err))
3675 3675 del fp
3676 3676 else:
3677 3677 for f in actions[b'revert'][0]:
3678 3678 prntstatusmsg(b'revert', f)
3679 3679 checkout(f)
3680 3680 if normal:
3681 3681 normal(f)
3682 3682
3683 3683 for f in actions[b'add'][0]:
3684 3684 # Don't checkout modified files, they are already created by the diff
3685 3685 if f not in newlyaddedandmodifiedfiles:
3686 3686 prntstatusmsg(b'add', f)
3687 3687 checkout(f)
3688 3688 repo.dirstate.add(f)
3689 3689
3690 3690 normal = repo.dirstate.normallookup
3691 3691 if node == parent and p2 == nullid:
3692 3692 normal = repo.dirstate.normal
3693 3693 for f in actions[b'undelete'][0]:
3694 3694 if interactive:
3695 3695 choice = repo.ui.promptchoice(
3696 3696 _(b"add back removed file %s (Yn)?$$ &Yes $$ &No") % f
3697 3697 )
3698 3698 if choice == 0:
3699 3699 prntstatusmsg(b'undelete', f)
3700 3700 checkout(f)
3701 3701 normal(f)
3702 3702 else:
3703 3703 excluded_files.append(f)
3704 3704 else:
3705 3705 prntstatusmsg(b'undelete', f)
3706 3706 checkout(f)
3707 3707 normal(f)
3708 3708
3709 3709 copied = copies.pathcopies(repo[parent], ctx)
3710 3710
3711 3711 for f in (
3712 3712 actions[b'add'][0] + actions[b'undelete'][0] + actions[b'revert'][0]
3713 3713 ):
3714 3714 if f in copied:
3715 3715 repo.dirstate.copy(copied[f], f)
3716 3716
3717 3717
3718 3718 # a list of (ui, repo, otherpeer, opts, missing) functions called by
3719 3719 # commands.outgoing. "missing" is "missing" of the result of
3720 3720 # "findcommonoutgoing()"
3721 3721 outgoinghooks = util.hooks()
3722 3722
3723 3723 # a list of (ui, repo) functions called by commands.summary
3724 3724 summaryhooks = util.hooks()
3725 3725
3726 3726 # a list of (ui, repo, opts, changes) functions called by commands.summary.
3727 3727 #
3728 3728 # functions should return tuple of booleans below, if 'changes' is None:
3729 3729 # (whether-incomings-are-needed, whether-outgoings-are-needed)
3730 3730 #
3731 3731 # otherwise, 'changes' is a tuple of tuples below:
3732 3732 # - (sourceurl, sourcebranch, sourcepeer, incoming)
3733 3733 # - (desturl, destbranch, destpeer, outgoing)
3734 3734 summaryremotehooks = util.hooks()
3735 3735
3736 3736
3737 3737 def checkunfinished(repo, commit=False, skipmerge=False):
3738 3738 """Look for an unfinished multistep operation, like graft, and abort
3739 3739 if found. It's probably good to check this right before
3740 3740 bailifchanged().
3741 3741 """
3742 3742 # Check for non-clearable states first, so things like rebase will take
3743 3743 # precedence over update.
3744 3744 for state in statemod._unfinishedstates:
3745 3745 if (
3746 3746 state._clearable
3747 3747 or (commit and state._allowcommit)
3748 3748 or state._reportonly
3749 3749 ):
3750 3750 continue
3751 3751 if state.isunfinished(repo):
3752 3752 raise error.StateError(state.msg(), hint=state.hint())
3753 3753
3754 3754 for s in statemod._unfinishedstates:
3755 3755 if (
3756 3756 not s._clearable
3757 3757 or (commit and s._allowcommit)
3758 3758 or (s._opname == b'merge' and skipmerge)
3759 3759 or s._reportonly
3760 3760 ):
3761 3761 continue
3762 3762 if s.isunfinished(repo):
3763 3763 raise error.StateError(s.msg(), hint=s.hint())
3764 3764
3765 3765
3766 3766 def clearunfinished(repo):
3767 3767 """Check for unfinished operations (as above), and clear the ones
3768 3768 that are clearable.
3769 3769 """
3770 3770 for state in statemod._unfinishedstates:
3771 3771 if state._reportonly:
3772 3772 continue
3773 3773 if not state._clearable and state.isunfinished(repo):
3774 3774 raise error.StateError(state.msg(), hint=state.hint())
3775 3775
3776 3776 for s in statemod._unfinishedstates:
3777 3777 if s._opname == b'merge' or state._reportonly:
3778 3778 continue
3779 3779 if s._clearable and s.isunfinished(repo):
3780 3780 util.unlink(repo.vfs.join(s._fname))
3781 3781
3782 3782
3783 3783 def getunfinishedstate(repo):
3784 3784 """Checks for unfinished operations and returns statecheck object
3785 3785 for it"""
3786 3786 for state in statemod._unfinishedstates:
3787 3787 if state.isunfinished(repo):
3788 3788 return state
3789 3789 return None
3790 3790
3791 3791
3792 3792 def howtocontinue(repo):
3793 3793 """Check for an unfinished operation and return the command to finish
3794 3794 it.
3795 3795
3796 3796 statemod._unfinishedstates list is checked for an unfinished operation
3797 3797 and the corresponding message to finish it is generated if a method to
3798 3798 continue is supported by the operation.
3799 3799
3800 3800 Returns a (msg, warning) tuple. 'msg' is a string and 'warning' is
3801 3801 a boolean.
3802 3802 """
3803 3803 contmsg = _(b"continue: %s")
3804 3804 for state in statemod._unfinishedstates:
3805 3805 if not state._continueflag:
3806 3806 continue
3807 3807 if state.isunfinished(repo):
3808 3808 return contmsg % state.continuemsg(), True
3809 3809 if repo[None].dirty(missing=True, merge=False, branch=False):
3810 3810 return contmsg % _(b"hg commit"), False
3811 3811 return None, None
3812 3812
3813 3813
3814 3814 def checkafterresolved(repo):
3815 3815 """Inform the user about the next action after completing hg resolve
3816 3816
3817 3817 If there's a an unfinished operation that supports continue flag,
3818 3818 howtocontinue will yield repo.ui.warn as the reporter.
3819 3819
3820 3820 Otherwise, it will yield repo.ui.note.
3821 3821 """
3822 3822 msg, warning = howtocontinue(repo)
3823 3823 if msg is not None:
3824 3824 if warning:
3825 3825 repo.ui.warn(b"%s\n" % msg)
3826 3826 else:
3827 3827 repo.ui.note(b"%s\n" % msg)
3828 3828
3829 3829
3830 3830 def wrongtooltocontinue(repo, task):
3831 3831 """Raise an abort suggesting how to properly continue if there is an
3832 3832 active task.
3833 3833
3834 3834 Uses howtocontinue() to find the active task.
3835 3835
3836 3836 If there's no task (repo.ui.note for 'hg commit'), it does not offer
3837 3837 a hint.
3838 3838 """
3839 3839 after = howtocontinue(repo)
3840 3840 hint = None
3841 3841 if after[1]:
3842 3842 hint = after[0]
3843 3843 raise error.StateError(_(b'no %s in progress') % task, hint=hint)
3844 3844
3845 3845
3846 3846 def abortgraft(ui, repo, graftstate):
3847 3847 """abort the interrupted graft and rollbacks to the state before interrupted
3848 3848 graft"""
3849 3849 if not graftstate.exists():
3850 3850 raise error.StateError(_(b"no interrupted graft to abort"))
3851 3851 statedata = readgraftstate(repo, graftstate)
3852 3852 newnodes = statedata.get(b'newnodes')
3853 3853 if newnodes is None:
3854 3854 # and old graft state which does not have all the data required to abort
3855 3855 # the graft
3856 3856 raise error.Abort(_(b"cannot abort using an old graftstate"))
3857 3857
3858 3858 # changeset from which graft operation was started
3859 3859 if len(newnodes) > 0:
3860 3860 startctx = repo[newnodes[0]].p1()
3861 3861 else:
3862 3862 startctx = repo[b'.']
3863 3863 # whether to strip or not
3864 3864 cleanup = False
3865 3865
3866 3866 if newnodes:
3867 3867 newnodes = [repo[r].rev() for r in newnodes]
3868 3868 cleanup = True
3869 3869 # checking that none of the newnodes turned public or is public
3870 3870 immutable = [c for c in newnodes if not repo[c].mutable()]
3871 3871 if immutable:
3872 3872 repo.ui.warn(
3873 3873 _(b"cannot clean up public changesets %s\n")
3874 3874 % b', '.join(bytes(repo[r]) for r in immutable),
3875 3875 hint=_(b"see 'hg help phases' for details"),
3876 3876 )
3877 3877 cleanup = False
3878 3878
3879 3879 # checking that no new nodes are created on top of grafted revs
3880 3880 desc = set(repo.changelog.descendants(newnodes))
3881 3881 if desc - set(newnodes):
3882 3882 repo.ui.warn(
3883 3883 _(
3884 3884 b"new changesets detected on destination "
3885 3885 b"branch, can't strip\n"
3886 3886 )
3887 3887 )
3888 3888 cleanup = False
3889 3889
3890 3890 if cleanup:
3891 3891 with repo.wlock(), repo.lock():
3892 3892 mergemod.clean_update(startctx)
3893 3893 # stripping the new nodes created
3894 3894 strippoints = [
3895 3895 c.node() for c in repo.set(b"roots(%ld)", newnodes)
3896 3896 ]
3897 3897 repair.strip(repo.ui, repo, strippoints, backup=False)
3898 3898
3899 3899 if not cleanup:
3900 3900 # we don't update to the startnode if we can't strip
3901 3901 startctx = repo[b'.']
3902 3902 mergemod.clean_update(startctx)
3903 3903
3904 3904 ui.status(_(b"graft aborted\n"))
3905 3905 ui.status(_(b"working directory is now at %s\n") % startctx.hex()[:12])
3906 3906 graftstate.delete()
3907 3907 return 0
3908 3908
3909 3909
3910 3910 def readgraftstate(repo, graftstate):
3911 3911 # type: (Any, statemod.cmdstate) -> Dict[bytes, Any]
3912 3912 """read the graft state file and return a dict of the data stored in it"""
3913 3913 try:
3914 3914 return graftstate.read()
3915 3915 except error.CorruptedState:
3916 3916 nodes = repo.vfs.read(b'graftstate').splitlines()
3917 3917 return {b'nodes': nodes}
3918 3918
3919 3919
3920 3920 def hgabortgraft(ui, repo):
3921 3921 """ abort logic for aborting graft using 'hg abort'"""
3922 3922 with repo.wlock():
3923 3923 graftstate = statemod.cmdstate(repo, b'graftstate')
3924 3924 return abortgraft(ui, repo, graftstate)
@@ -1,2605 +1,2611 b''
1 1 # configitems.py - centralized declaration of configuration option
2 2 #
3 3 # Copyright 2017 Pierre-Yves David <pierre-yves.david@octobus.net>
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 absolute_import
9 9
10 10 import functools
11 11 import re
12 12
13 13 from . import (
14 14 encoding,
15 15 error,
16 16 )
17 17
18 18
19 19 def loadconfigtable(ui, extname, configtable):
20 20 """update config item known to the ui with the extension ones"""
21 21 for section, items in sorted(configtable.items()):
22 22 knownitems = ui._knownconfig.setdefault(section, itemregister())
23 23 knownkeys = set(knownitems)
24 24 newkeys = set(items)
25 25 for key in sorted(knownkeys & newkeys):
26 26 msg = b"extension '%s' overwrite config item '%s.%s'"
27 27 msg %= (extname, section, key)
28 28 ui.develwarn(msg, config=b'warn-config')
29 29
30 30 knownitems.update(items)
31 31
32 32
33 33 class configitem(object):
34 34 """represent a known config item
35 35
36 36 :section: the official config section where to find this item,
37 37 :name: the official name within the section,
38 38 :default: default value for this item,
39 39 :alias: optional list of tuples as alternatives,
40 40 :generic: this is a generic definition, match name using regular expression.
41 41 """
42 42
43 43 def __init__(
44 44 self,
45 45 section,
46 46 name,
47 47 default=None,
48 48 alias=(),
49 49 generic=False,
50 50 priority=0,
51 51 experimental=False,
52 52 ):
53 53 self.section = section
54 54 self.name = name
55 55 self.default = default
56 56 self.alias = list(alias)
57 57 self.generic = generic
58 58 self.priority = priority
59 59 self.experimental = experimental
60 60 self._re = None
61 61 if generic:
62 62 self._re = re.compile(self.name)
63 63
64 64
65 65 class itemregister(dict):
66 66 """A specialized dictionary that can handle wild-card selection"""
67 67
68 68 def __init__(self):
69 69 super(itemregister, self).__init__()
70 70 self._generics = set()
71 71
72 72 def update(self, other):
73 73 super(itemregister, self).update(other)
74 74 self._generics.update(other._generics)
75 75
76 76 def __setitem__(self, key, item):
77 77 super(itemregister, self).__setitem__(key, item)
78 78 if item.generic:
79 79 self._generics.add(item)
80 80
81 81 def get(self, key):
82 82 baseitem = super(itemregister, self).get(key)
83 83 if baseitem is not None and not baseitem.generic:
84 84 return baseitem
85 85
86 86 # search for a matching generic item
87 87 generics = sorted(self._generics, key=(lambda x: (x.priority, x.name)))
88 88 for item in generics:
89 89 # we use 'match' instead of 'search' to make the matching simpler
90 90 # for people unfamiliar with regular expression. Having the match
91 91 # rooted to the start of the string will produce less surprising
92 92 # result for user writing simple regex for sub-attribute.
93 93 #
94 94 # For example using "color\..*" match produces an unsurprising
95 95 # result, while using search could suddenly match apparently
96 96 # unrelated configuration that happens to contains "color."
97 97 # anywhere. This is a tradeoff where we favor requiring ".*" on
98 98 # some match to avoid the need to prefix most pattern with "^".
99 99 # The "^" seems more error prone.
100 100 if item._re.match(key):
101 101 return item
102 102
103 103 return None
104 104
105 105
106 106 coreitems = {}
107 107
108 108
109 109 def _register(configtable, *args, **kwargs):
110 110 item = configitem(*args, **kwargs)
111 111 section = configtable.setdefault(item.section, itemregister())
112 112 if item.name in section:
113 113 msg = b"duplicated config item registration for '%s.%s'"
114 114 raise error.ProgrammingError(msg % (item.section, item.name))
115 115 section[item.name] = item
116 116
117 117
118 118 # special value for case where the default is derived from other values
119 119 dynamicdefault = object()
120 120
121 121 # Registering actual config items
122 122
123 123
124 124 def getitemregister(configtable):
125 125 f = functools.partial(_register, configtable)
126 126 # export pseudo enum as configitem.*
127 127 f.dynamicdefault = dynamicdefault
128 128 return f
129 129
130 130
131 131 coreconfigitem = getitemregister(coreitems)
132 132
133 133
134 134 def _registerdiffopts(section, configprefix=b''):
135 135 coreconfigitem(
136 136 section,
137 137 configprefix + b'nodates',
138 138 default=False,
139 139 )
140 140 coreconfigitem(
141 141 section,
142 142 configprefix + b'showfunc',
143 143 default=False,
144 144 )
145 145 coreconfigitem(
146 146 section,
147 147 configprefix + b'unified',
148 148 default=None,
149 149 )
150 150 coreconfigitem(
151 151 section,
152 152 configprefix + b'git',
153 153 default=False,
154 154 )
155 155 coreconfigitem(
156 156 section,
157 157 configprefix + b'ignorews',
158 158 default=False,
159 159 )
160 160 coreconfigitem(
161 161 section,
162 162 configprefix + b'ignorewsamount',
163 163 default=False,
164 164 )
165 165 coreconfigitem(
166 166 section,
167 167 configprefix + b'ignoreblanklines',
168 168 default=False,
169 169 )
170 170 coreconfigitem(
171 171 section,
172 172 configprefix + b'ignorewseol',
173 173 default=False,
174 174 )
175 175 coreconfigitem(
176 176 section,
177 177 configprefix + b'nobinary',
178 178 default=False,
179 179 )
180 180 coreconfigitem(
181 181 section,
182 182 configprefix + b'noprefix',
183 183 default=False,
184 184 )
185 185 coreconfigitem(
186 186 section,
187 187 configprefix + b'word-diff',
188 188 default=False,
189 189 )
190 190
191 191
192 192 coreconfigitem(
193 193 b'alias',
194 194 b'.*',
195 195 default=dynamicdefault,
196 196 generic=True,
197 197 )
198 198 coreconfigitem(
199 199 b'auth',
200 200 b'cookiefile',
201 201 default=None,
202 202 )
203 203 _registerdiffopts(section=b'annotate')
204 204 # bookmarks.pushing: internal hack for discovery
205 205 coreconfigitem(
206 206 b'bookmarks',
207 207 b'pushing',
208 208 default=list,
209 209 )
210 210 # bundle.mainreporoot: internal hack for bundlerepo
211 211 coreconfigitem(
212 212 b'bundle',
213 213 b'mainreporoot',
214 214 default=b'',
215 215 )
216 216 coreconfigitem(
217 217 b'censor',
218 218 b'policy',
219 219 default=b'abort',
220 220 experimental=True,
221 221 )
222 222 coreconfigitem(
223 223 b'chgserver',
224 224 b'idletimeout',
225 225 default=3600,
226 226 )
227 227 coreconfigitem(
228 228 b'chgserver',
229 229 b'skiphash',
230 230 default=False,
231 231 )
232 232 coreconfigitem(
233 233 b'cmdserver',
234 234 b'log',
235 235 default=None,
236 236 )
237 237 coreconfigitem(
238 238 b'cmdserver',
239 239 b'max-log-files',
240 240 default=7,
241 241 )
242 242 coreconfigitem(
243 243 b'cmdserver',
244 244 b'max-log-size',
245 245 default=b'1 MB',
246 246 )
247 247 coreconfigitem(
248 248 b'cmdserver',
249 249 b'max-repo-cache',
250 250 default=0,
251 251 experimental=True,
252 252 )
253 253 coreconfigitem(
254 254 b'cmdserver',
255 255 b'message-encodings',
256 256 default=list,
257 257 )
258 258 coreconfigitem(
259 259 b'cmdserver',
260 260 b'track-log',
261 261 default=lambda: [b'chgserver', b'cmdserver', b'repocache'],
262 262 )
263 263 coreconfigitem(
264 264 b'cmdserver',
265 265 b'shutdown-on-interrupt',
266 266 default=True,
267 267 )
268 268 coreconfigitem(
269 269 b'color',
270 270 b'.*',
271 271 default=None,
272 272 generic=True,
273 273 )
274 274 coreconfigitem(
275 275 b'color',
276 276 b'mode',
277 277 default=b'auto',
278 278 )
279 279 coreconfigitem(
280 280 b'color',
281 281 b'pagermode',
282 282 default=dynamicdefault,
283 283 )
284 284 coreconfigitem(
285 285 b'command-templates',
286 286 b'graphnode',
287 287 default=None,
288 288 alias=[(b'ui', b'graphnodetemplate')],
289 289 )
290 290 coreconfigitem(
291 291 b'command-templates',
292 292 b'log',
293 293 default=None,
294 294 alias=[(b'ui', b'logtemplate')],
295 295 )
296 296 coreconfigitem(
297 297 b'command-templates',
298 298 b'mergemarker',
299 299 default=(
300 300 b'{node|short} '
301 301 b'{ifeq(tags, "tip", "", '
302 302 b'ifeq(tags, "", "", "{tags} "))}'
303 303 b'{if(bookmarks, "{bookmarks} ")}'
304 304 b'{ifeq(branch, "default", "", "{branch} ")}'
305 305 b'- {author|user}: {desc|firstline}'
306 306 ),
307 307 alias=[(b'ui', b'mergemarkertemplate')],
308 308 )
309 309 coreconfigitem(
310 310 b'command-templates',
311 311 b'pre-merge-tool-output',
312 312 default=None,
313 313 alias=[(b'ui', b'pre-merge-tool-output-template')],
314 314 )
315 315 coreconfigitem(
316 316 b'command-templates',
317 317 b'oneline-summary',
318 318 default=None,
319 319 )
320 320 coreconfigitem(
321 321 b'command-templates',
322 322 b'oneline-summary.*',
323 323 default=dynamicdefault,
324 324 generic=True,
325 325 )
326 326 _registerdiffopts(section=b'commands', configprefix=b'commit.interactive.')
327 327 coreconfigitem(
328 328 b'commands',
329 329 b'commit.post-status',
330 330 default=False,
331 331 )
332 332 coreconfigitem(
333 333 b'commands',
334 334 b'grep.all-files',
335 335 default=False,
336 336 experimental=True,
337 337 )
338 338 coreconfigitem(
339 339 b'commands',
340 340 b'merge.require-rev',
341 341 default=False,
342 342 )
343 343 coreconfigitem(
344 344 b'commands',
345 345 b'push.require-revs',
346 346 default=False,
347 347 )
348 348 coreconfigitem(
349 349 b'commands',
350 350 b'resolve.confirm',
351 351 default=False,
352 352 )
353 353 coreconfigitem(
354 354 b'commands',
355 355 b'resolve.explicit-re-merge',
356 356 default=False,
357 357 )
358 358 coreconfigitem(
359 359 b'commands',
360 360 b'resolve.mark-check',
361 361 default=b'none',
362 362 )
363 363 _registerdiffopts(section=b'commands', configprefix=b'revert.interactive.')
364 364 coreconfigitem(
365 365 b'commands',
366 366 b'show.aliasprefix',
367 367 default=list,
368 368 )
369 369 coreconfigitem(
370 370 b'commands',
371 371 b'status.relative',
372 372 default=False,
373 373 )
374 374 coreconfigitem(
375 375 b'commands',
376 376 b'status.skipstates',
377 377 default=[],
378 378 experimental=True,
379 379 )
380 380 coreconfigitem(
381 381 b'commands',
382 382 b'status.terse',
383 383 default=b'',
384 384 )
385 385 coreconfigitem(
386 386 b'commands',
387 387 b'status.verbose',
388 388 default=False,
389 389 )
390 390 coreconfigitem(
391 391 b'commands',
392 392 b'update.check',
393 393 default=None,
394 394 )
395 395 coreconfigitem(
396 396 b'commands',
397 397 b'update.requiredest',
398 398 default=False,
399 399 )
400 400 coreconfigitem(
401 401 b'committemplate',
402 402 b'.*',
403 403 default=None,
404 404 generic=True,
405 405 )
406 406 coreconfigitem(
407 407 b'convert',
408 408 b'bzr.saverev',
409 409 default=True,
410 410 )
411 411 coreconfigitem(
412 412 b'convert',
413 413 b'cvsps.cache',
414 414 default=True,
415 415 )
416 416 coreconfigitem(
417 417 b'convert',
418 418 b'cvsps.fuzz',
419 419 default=60,
420 420 )
421 421 coreconfigitem(
422 422 b'convert',
423 423 b'cvsps.logencoding',
424 424 default=None,
425 425 )
426 426 coreconfigitem(
427 427 b'convert',
428 428 b'cvsps.mergefrom',
429 429 default=None,
430 430 )
431 431 coreconfigitem(
432 432 b'convert',
433 433 b'cvsps.mergeto',
434 434 default=None,
435 435 )
436 436 coreconfigitem(
437 437 b'convert',
438 438 b'git.committeractions',
439 439 default=lambda: [b'messagedifferent'],
440 440 )
441 441 coreconfigitem(
442 442 b'convert',
443 443 b'git.extrakeys',
444 444 default=list,
445 445 )
446 446 coreconfigitem(
447 447 b'convert',
448 448 b'git.findcopiesharder',
449 449 default=False,
450 450 )
451 451 coreconfigitem(
452 452 b'convert',
453 453 b'git.remoteprefix',
454 454 default=b'remote',
455 455 )
456 456 coreconfigitem(
457 457 b'convert',
458 458 b'git.renamelimit',
459 459 default=400,
460 460 )
461 461 coreconfigitem(
462 462 b'convert',
463 463 b'git.saverev',
464 464 default=True,
465 465 )
466 466 coreconfigitem(
467 467 b'convert',
468 468 b'git.similarity',
469 469 default=50,
470 470 )
471 471 coreconfigitem(
472 472 b'convert',
473 473 b'git.skipsubmodules',
474 474 default=False,
475 475 )
476 476 coreconfigitem(
477 477 b'convert',
478 478 b'hg.clonebranches',
479 479 default=False,
480 480 )
481 481 coreconfigitem(
482 482 b'convert',
483 483 b'hg.ignoreerrors',
484 484 default=False,
485 485 )
486 486 coreconfigitem(
487 487 b'convert',
488 488 b'hg.preserve-hash',
489 489 default=False,
490 490 )
491 491 coreconfigitem(
492 492 b'convert',
493 493 b'hg.revs',
494 494 default=None,
495 495 )
496 496 coreconfigitem(
497 497 b'convert',
498 498 b'hg.saverev',
499 499 default=False,
500 500 )
501 501 coreconfigitem(
502 502 b'convert',
503 503 b'hg.sourcename',
504 504 default=None,
505 505 )
506 506 coreconfigitem(
507 507 b'convert',
508 508 b'hg.startrev',
509 509 default=None,
510 510 )
511 511 coreconfigitem(
512 512 b'convert',
513 513 b'hg.tagsbranch',
514 514 default=b'default',
515 515 )
516 516 coreconfigitem(
517 517 b'convert',
518 518 b'hg.usebranchnames',
519 519 default=True,
520 520 )
521 521 coreconfigitem(
522 522 b'convert',
523 523 b'ignoreancestorcheck',
524 524 default=False,
525 525 experimental=True,
526 526 )
527 527 coreconfigitem(
528 528 b'convert',
529 529 b'localtimezone',
530 530 default=False,
531 531 )
532 532 coreconfigitem(
533 533 b'convert',
534 534 b'p4.encoding',
535 535 default=dynamicdefault,
536 536 )
537 537 coreconfigitem(
538 538 b'convert',
539 539 b'p4.startrev',
540 540 default=0,
541 541 )
542 542 coreconfigitem(
543 543 b'convert',
544 544 b'skiptags',
545 545 default=False,
546 546 )
547 547 coreconfigitem(
548 548 b'convert',
549 549 b'svn.debugsvnlog',
550 550 default=True,
551 551 )
552 552 coreconfigitem(
553 553 b'convert',
554 554 b'svn.trunk',
555 555 default=None,
556 556 )
557 557 coreconfigitem(
558 558 b'convert',
559 559 b'svn.tags',
560 560 default=None,
561 561 )
562 562 coreconfigitem(
563 563 b'convert',
564 564 b'svn.branches',
565 565 default=None,
566 566 )
567 567 coreconfigitem(
568 568 b'convert',
569 569 b'svn.startrev',
570 570 default=0,
571 571 )
572 572 coreconfigitem(
573 573 b'convert',
574 574 b'svn.dangerous-set-commit-dates',
575 575 default=False,
576 576 )
577 577 coreconfigitem(
578 578 b'debug',
579 579 b'dirstate.delaywrite',
580 580 default=0,
581 581 )
582 582 coreconfigitem(
583 583 b'defaults',
584 584 b'.*',
585 585 default=None,
586 586 generic=True,
587 587 )
588 588 coreconfigitem(
589 589 b'devel',
590 590 b'all-warnings',
591 591 default=False,
592 592 )
593 593 coreconfigitem(
594 594 b'devel',
595 595 b'bundle2.debug',
596 596 default=False,
597 597 )
598 598 coreconfigitem(
599 599 b'devel',
600 600 b'bundle.delta',
601 601 default=b'',
602 602 )
603 603 coreconfigitem(
604 604 b'devel',
605 605 b'cache-vfs',
606 606 default=None,
607 607 )
608 608 coreconfigitem(
609 609 b'devel',
610 610 b'check-locks',
611 611 default=False,
612 612 )
613 613 coreconfigitem(
614 614 b'devel',
615 615 b'check-relroot',
616 616 default=False,
617 617 )
618 618 # Track copy information for all file, not just "added" one (very slow)
619 619 coreconfigitem(
620 620 b'devel',
621 621 b'copy-tracing.trace-all-files',
622 622 default=False,
623 623 )
624 624 coreconfigitem(
625 625 b'devel',
626 626 b'default-date',
627 627 default=None,
628 628 )
629 629 coreconfigitem(
630 630 b'devel',
631 631 b'deprec-warn',
632 632 default=False,
633 633 )
634 634 coreconfigitem(
635 635 b'devel',
636 636 b'disableloaddefaultcerts',
637 637 default=False,
638 638 )
639 639 coreconfigitem(
640 640 b'devel',
641 641 b'warn-empty-changegroup',
642 642 default=False,
643 643 )
644 644 coreconfigitem(
645 645 b'devel',
646 646 b'legacy.exchange',
647 647 default=list,
648 648 )
649 649 # When True, revlogs use a special reference version of the nodemap, that is not
650 650 # performant but is "known" to behave properly.
651 651 coreconfigitem(
652 652 b'devel',
653 653 b'persistent-nodemap',
654 654 default=False,
655 655 )
656 656 coreconfigitem(
657 657 b'devel',
658 658 b'servercafile',
659 659 default=b'',
660 660 )
661 661 coreconfigitem(
662 662 b'devel',
663 663 b'serverexactprotocol',
664 664 default=b'',
665 665 )
666 666 coreconfigitem(
667 667 b'devel',
668 668 b'serverrequirecert',
669 669 default=False,
670 670 )
671 671 coreconfigitem(
672 672 b'devel',
673 673 b'strip-obsmarkers',
674 674 default=True,
675 675 )
676 676 coreconfigitem(
677 677 b'devel',
678 678 b'warn-config',
679 679 default=None,
680 680 )
681 681 coreconfigitem(
682 682 b'devel',
683 683 b'warn-config-default',
684 684 default=None,
685 685 )
686 686 coreconfigitem(
687 687 b'devel',
688 688 b'user.obsmarker',
689 689 default=None,
690 690 )
691 691 coreconfigitem(
692 692 b'devel',
693 693 b'warn-config-unknown',
694 694 default=None,
695 695 )
696 696 coreconfigitem(
697 697 b'devel',
698 698 b'debug.copies',
699 699 default=False,
700 700 )
701 701 coreconfigitem(
702 702 b'devel',
703 703 b'debug.extensions',
704 704 default=False,
705 705 )
706 706 coreconfigitem(
707 707 b'devel',
708 708 b'debug.repo-filters',
709 709 default=False,
710 710 )
711 711 coreconfigitem(
712 712 b'devel',
713 713 b'debug.peer-request',
714 714 default=False,
715 715 )
716 716 # If discovery.exchange-heads is False, the discovery will not start with
717 717 # remote head fetching and local head querying.
718 718 coreconfigitem(
719 719 b'devel',
720 720 b'discovery.exchange-heads',
721 721 default=True,
722 722 )
723 723 # If discovery.grow-sample is False, the sample size used in set discovery will
724 724 # not be increased through the process
725 725 coreconfigitem(
726 726 b'devel',
727 727 b'discovery.grow-sample',
728 728 default=True,
729 729 )
730 730 # discovery.grow-sample.rate control the rate at which the sample grow
731 731 coreconfigitem(
732 732 b'devel',
733 733 b'discovery.grow-sample.rate',
734 734 default=1.05,
735 735 )
736 736 # If discovery.randomize is False, random sampling during discovery are
737 737 # deterministic. It is meant for integration tests.
738 738 coreconfigitem(
739 739 b'devel',
740 740 b'discovery.randomize',
741 741 default=True,
742 742 )
743 743 # Control the initial size of the discovery sample
744 744 coreconfigitem(
745 745 b'devel',
746 746 b'discovery.sample-size',
747 747 default=200,
748 748 )
749 749 # Control the initial size of the discovery for initial change
750 750 coreconfigitem(
751 751 b'devel',
752 752 b'discovery.sample-size.initial',
753 753 default=100,
754 754 )
755 755 _registerdiffopts(section=b'diff')
756 756 coreconfigitem(
757 757 b'email',
758 758 b'bcc',
759 759 default=None,
760 760 )
761 761 coreconfigitem(
762 762 b'email',
763 763 b'cc',
764 764 default=None,
765 765 )
766 766 coreconfigitem(
767 767 b'email',
768 768 b'charsets',
769 769 default=list,
770 770 )
771 771 coreconfigitem(
772 772 b'email',
773 773 b'from',
774 774 default=None,
775 775 )
776 776 coreconfigitem(
777 777 b'email',
778 778 b'method',
779 779 default=b'smtp',
780 780 )
781 781 coreconfigitem(
782 782 b'email',
783 783 b'reply-to',
784 784 default=None,
785 785 )
786 786 coreconfigitem(
787 787 b'email',
788 788 b'to',
789 789 default=None,
790 790 )
791 791 coreconfigitem(
792 792 b'experimental',
793 793 b'archivemetatemplate',
794 794 default=dynamicdefault,
795 795 )
796 796 coreconfigitem(
797 797 b'experimental',
798 798 b'auto-publish',
799 799 default=b'publish',
800 800 )
801 801 coreconfigitem(
802 802 b'experimental',
803 803 b'bundle-phases',
804 804 default=False,
805 805 )
806 806 coreconfigitem(
807 807 b'experimental',
808 808 b'bundle2-advertise',
809 809 default=True,
810 810 )
811 811 coreconfigitem(
812 812 b'experimental',
813 813 b'bundle2-output-capture',
814 814 default=False,
815 815 )
816 816 coreconfigitem(
817 817 b'experimental',
818 818 b'bundle2.pushback',
819 819 default=False,
820 820 )
821 821 coreconfigitem(
822 822 b'experimental',
823 823 b'bundle2lazylocking',
824 824 default=False,
825 825 )
826 826 coreconfigitem(
827 827 b'experimental',
828 828 b'bundlecomplevel',
829 829 default=None,
830 830 )
831 831 coreconfigitem(
832 832 b'experimental',
833 833 b'bundlecomplevel.bzip2',
834 834 default=None,
835 835 )
836 836 coreconfigitem(
837 837 b'experimental',
838 838 b'bundlecomplevel.gzip',
839 839 default=None,
840 840 )
841 841 coreconfigitem(
842 842 b'experimental',
843 843 b'bundlecomplevel.none',
844 844 default=None,
845 845 )
846 846 coreconfigitem(
847 847 b'experimental',
848 848 b'bundlecomplevel.zstd',
849 849 default=None,
850 850 )
851 851 coreconfigitem(
852 852 b'experimental',
853 853 b'changegroup3',
854 854 default=False,
855 855 )
856 856 coreconfigitem(
857 857 b'experimental',
858 858 b'cleanup-as-archived',
859 859 default=False,
860 860 )
861 861 coreconfigitem(
862 862 b'experimental',
863 863 b'clientcompressionengines',
864 864 default=list,
865 865 )
866 866 coreconfigitem(
867 867 b'experimental',
868 868 b'copytrace',
869 869 default=b'on',
870 870 )
871 871 coreconfigitem(
872 872 b'experimental',
873 873 b'copytrace.movecandidateslimit',
874 874 default=100,
875 875 )
876 876 coreconfigitem(
877 877 b'experimental',
878 878 b'copytrace.sourcecommitlimit',
879 879 default=100,
880 880 )
881 881 coreconfigitem(
882 882 b'experimental',
883 883 b'copies.read-from',
884 884 default=b"filelog-only",
885 885 )
886 886 coreconfigitem(
887 887 b'experimental',
888 888 b'copies.write-to',
889 889 default=b'filelog-only',
890 890 )
891 891 coreconfigitem(
892 892 b'experimental',
893 893 b'crecordtest',
894 894 default=None,
895 895 )
896 896 coreconfigitem(
897 897 b'experimental',
898 898 b'directaccess',
899 899 default=False,
900 900 )
901 901 coreconfigitem(
902 902 b'experimental',
903 903 b'directaccess.revnums',
904 904 default=False,
905 905 )
906 906 coreconfigitem(
907 907 b'experimental',
908 908 b'editortmpinhg',
909 909 default=False,
910 910 )
911 911 coreconfigitem(
912 912 b'experimental',
913 913 b'evolution',
914 914 default=list,
915 915 )
916 916 coreconfigitem(
917 917 b'experimental',
918 918 b'evolution.allowdivergence',
919 919 default=False,
920 920 alias=[(b'experimental', b'allowdivergence')],
921 921 )
922 922 coreconfigitem(
923 923 b'experimental',
924 924 b'evolution.allowunstable',
925 925 default=None,
926 926 )
927 927 coreconfigitem(
928 928 b'experimental',
929 929 b'evolution.createmarkers',
930 930 default=None,
931 931 )
932 932 coreconfigitem(
933 933 b'experimental',
934 934 b'evolution.effect-flags',
935 935 default=True,
936 936 alias=[(b'experimental', b'effect-flags')],
937 937 )
938 938 coreconfigitem(
939 939 b'experimental',
940 940 b'evolution.exchange',
941 941 default=None,
942 942 )
943 943 coreconfigitem(
944 944 b'experimental',
945 945 b'evolution.bundle-obsmarker',
946 946 default=False,
947 947 )
948 948 coreconfigitem(
949 949 b'experimental',
950 950 b'evolution.bundle-obsmarker:mandatory',
951 951 default=True,
952 952 )
953 953 coreconfigitem(
954 954 b'experimental',
955 955 b'log.topo',
956 956 default=False,
957 957 )
958 958 coreconfigitem(
959 959 b'experimental',
960 960 b'evolution.report-instabilities',
961 961 default=True,
962 962 )
963 963 coreconfigitem(
964 964 b'experimental',
965 965 b'evolution.track-operation',
966 966 default=True,
967 967 )
968 968 # repo-level config to exclude a revset visibility
969 969 #
970 970 # The target use case is to use `share` to expose different subset of the same
971 971 # repository, especially server side. See also `server.view`.
972 972 coreconfigitem(
973 973 b'experimental',
974 974 b'extra-filter-revs',
975 975 default=None,
976 976 )
977 977 coreconfigitem(
978 978 b'experimental',
979 979 b'maxdeltachainspan',
980 980 default=-1,
981 981 )
982 982 # tracks files which were undeleted (merge might delete them but we explicitly
983 983 # kept/undeleted them) and creates new filenodes for them
984 984 coreconfigitem(
985 985 b'experimental',
986 986 b'merge-track-salvaged',
987 987 default=False,
988 988 )
989 989 coreconfigitem(
990 990 b'experimental',
991 991 b'mergetempdirprefix',
992 992 default=None,
993 993 )
994 994 coreconfigitem(
995 995 b'experimental',
996 996 b'mmapindexthreshold',
997 997 default=None,
998 998 )
999 999 coreconfigitem(
1000 1000 b'experimental',
1001 1001 b'narrow',
1002 1002 default=False,
1003 1003 )
1004 1004 coreconfigitem(
1005 1005 b'experimental',
1006 1006 b'nonnormalparanoidcheck',
1007 1007 default=False,
1008 1008 )
1009 1009 coreconfigitem(
1010 1010 b'experimental',
1011 1011 b'exportableenviron',
1012 1012 default=list,
1013 1013 )
1014 1014 coreconfigitem(
1015 1015 b'experimental',
1016 1016 b'extendedheader.index',
1017 1017 default=None,
1018 1018 )
1019 1019 coreconfigitem(
1020 1020 b'experimental',
1021 1021 b'extendedheader.similarity',
1022 1022 default=False,
1023 1023 )
1024 1024 coreconfigitem(
1025 1025 b'experimental',
1026 1026 b'graphshorten',
1027 1027 default=False,
1028 1028 )
1029 1029 coreconfigitem(
1030 1030 b'experimental',
1031 1031 b'graphstyle.parent',
1032 1032 default=dynamicdefault,
1033 1033 )
1034 1034 coreconfigitem(
1035 1035 b'experimental',
1036 1036 b'graphstyle.missing',
1037 1037 default=dynamicdefault,
1038 1038 )
1039 1039 coreconfigitem(
1040 1040 b'experimental',
1041 1041 b'graphstyle.grandparent',
1042 1042 default=dynamicdefault,
1043 1043 )
1044 1044 coreconfigitem(
1045 1045 b'experimental',
1046 1046 b'hook-track-tags',
1047 1047 default=False,
1048 1048 )
1049 1049 coreconfigitem(
1050 1050 b'experimental',
1051 1051 b'httppeer.advertise-v2',
1052 1052 default=False,
1053 1053 )
1054 1054 coreconfigitem(
1055 1055 b'experimental',
1056 1056 b'httppeer.v2-encoder-order',
1057 1057 default=None,
1058 1058 )
1059 1059 coreconfigitem(
1060 1060 b'experimental',
1061 1061 b'httppostargs',
1062 1062 default=False,
1063 1063 )
1064 1064 coreconfigitem(b'experimental', b'nointerrupt', default=False)
1065 1065 coreconfigitem(b'experimental', b'nointerrupt-interactiveonly', default=True)
1066 1066
1067 1067 coreconfigitem(
1068 1068 b'experimental',
1069 1069 b'obsmarkers-exchange-debug',
1070 1070 default=False,
1071 1071 )
1072 1072 coreconfigitem(
1073 1073 b'experimental',
1074 1074 b'remotenames',
1075 1075 default=False,
1076 1076 )
1077 1077 coreconfigitem(
1078 1078 b'experimental',
1079 1079 b'removeemptydirs',
1080 1080 default=True,
1081 1081 )
1082 1082 coreconfigitem(
1083 1083 b'experimental',
1084 1084 b'revert.interactive.select-to-keep',
1085 1085 default=False,
1086 1086 )
1087 1087 coreconfigitem(
1088 1088 b'experimental',
1089 1089 b'revisions.prefixhexnode',
1090 1090 default=False,
1091 1091 )
1092 1092 coreconfigitem(
1093 1093 b'experimental',
1094 1094 b'revlogv2',
1095 1095 default=None,
1096 1096 )
1097 1097 coreconfigitem(
1098 1098 b'experimental',
1099 1099 b'revisions.disambiguatewithin',
1100 1100 default=None,
1101 1101 )
1102 1102 coreconfigitem(
1103 1103 b'experimental',
1104 1104 b'rust.index',
1105 1105 default=False,
1106 1106 )
1107 1107 coreconfigitem(
1108 1108 b'experimental',
1109 1109 b'server.filesdata.recommended-batch-size',
1110 1110 default=50000,
1111 1111 )
1112 1112 coreconfigitem(
1113 1113 b'experimental',
1114 1114 b'server.manifestdata.recommended-batch-size',
1115 1115 default=100000,
1116 1116 )
1117 1117 coreconfigitem(
1118 1118 b'experimental',
1119 1119 b'server.stream-narrow-clones',
1120 1120 default=False,
1121 1121 )
1122 1122 coreconfigitem(
1123 1123 b'experimental',
1124 1124 b'single-head-per-branch',
1125 1125 default=False,
1126 1126 )
1127 1127 coreconfigitem(
1128 1128 b'experimental',
1129 1129 b'single-head-per-branch:account-closed-heads',
1130 1130 default=False,
1131 1131 )
1132 1132 coreconfigitem(
1133 1133 b'experimental',
1134 1134 b'single-head-per-branch:public-changes-only',
1135 1135 default=False,
1136 1136 )
1137 1137 coreconfigitem(
1138 1138 b'experimental',
1139 1139 b'sshserver.support-v2',
1140 1140 default=False,
1141 1141 )
1142 1142 coreconfigitem(
1143 1143 b'experimental',
1144 1144 b'sparse-read',
1145 1145 default=False,
1146 1146 )
1147 1147 coreconfigitem(
1148 1148 b'experimental',
1149 1149 b'sparse-read.density-threshold',
1150 1150 default=0.50,
1151 1151 )
1152 1152 coreconfigitem(
1153 1153 b'experimental',
1154 1154 b'sparse-read.min-gap-size',
1155 1155 default=b'65K',
1156 1156 )
1157 1157 coreconfigitem(
1158 1158 b'experimental',
1159 1159 b'treemanifest',
1160 1160 default=False,
1161 1161 )
1162 1162 coreconfigitem(
1163 1163 b'experimental',
1164 1164 b'update.atomic-file',
1165 1165 default=False,
1166 1166 )
1167 1167 coreconfigitem(
1168 1168 b'experimental',
1169 1169 b'sshpeer.advertise-v2',
1170 1170 default=False,
1171 1171 )
1172 1172 coreconfigitem(
1173 1173 b'experimental',
1174 1174 b'web.apiserver',
1175 1175 default=False,
1176 1176 )
1177 1177 coreconfigitem(
1178 1178 b'experimental',
1179 1179 b'web.api.http-v2',
1180 1180 default=False,
1181 1181 )
1182 1182 coreconfigitem(
1183 1183 b'experimental',
1184 1184 b'web.api.debugreflect',
1185 1185 default=False,
1186 1186 )
1187 1187 coreconfigitem(
1188 1188 b'experimental',
1189 1189 b'worker.wdir-get-thread-safe',
1190 1190 default=False,
1191 1191 )
1192 1192 coreconfigitem(
1193 1193 b'experimental',
1194 1194 b'worker.repository-upgrade',
1195 1195 default=False,
1196 1196 )
1197 1197 coreconfigitem(
1198 1198 b'experimental',
1199 1199 b'xdiff',
1200 1200 default=False,
1201 1201 )
1202 1202 coreconfigitem(
1203 1203 b'extensions',
1204 1204 b'.*',
1205 1205 default=None,
1206 1206 generic=True,
1207 1207 )
1208 1208 coreconfigitem(
1209 1209 b'extdata',
1210 1210 b'.*',
1211 1211 default=None,
1212 1212 generic=True,
1213 1213 )
1214 1214 coreconfigitem(
1215 1215 b'format',
1216 1216 b'bookmarks-in-store',
1217 1217 default=False,
1218 1218 )
1219 1219 coreconfigitem(
1220 1220 b'format',
1221 1221 b'chunkcachesize',
1222 1222 default=None,
1223 1223 experimental=True,
1224 1224 )
1225 1225 coreconfigitem(
1226 1226 b'format',
1227 1227 b'dotencode',
1228 1228 default=True,
1229 1229 )
1230 1230 coreconfigitem(
1231 1231 b'format',
1232 1232 b'generaldelta',
1233 1233 default=False,
1234 1234 experimental=True,
1235 1235 )
1236 1236 coreconfigitem(
1237 1237 b'format',
1238 1238 b'manifestcachesize',
1239 1239 default=None,
1240 1240 experimental=True,
1241 1241 )
1242 1242 coreconfigitem(
1243 1243 b'format',
1244 1244 b'maxchainlen',
1245 1245 default=dynamicdefault,
1246 1246 experimental=True,
1247 1247 )
1248 1248 coreconfigitem(
1249 1249 b'format',
1250 1250 b'obsstore-version',
1251 1251 default=None,
1252 1252 )
1253 1253 coreconfigitem(
1254 1254 b'format',
1255 1255 b'sparse-revlog',
1256 1256 default=True,
1257 1257 )
1258 1258 coreconfigitem(
1259 1259 b'format',
1260 1260 b'revlog-compression',
1261 1261 default=lambda: [b'zlib'],
1262 1262 alias=[(b'experimental', b'format.compression')],
1263 1263 )
1264 1264 coreconfigitem(
1265 1265 b'format',
1266 1266 b'usefncache',
1267 1267 default=True,
1268 1268 )
1269 1269 coreconfigitem(
1270 1270 b'format',
1271 1271 b'usegeneraldelta',
1272 1272 default=True,
1273 1273 )
1274 1274 coreconfigitem(
1275 1275 b'format',
1276 1276 b'usestore',
1277 1277 default=True,
1278 1278 )
1279 1279 coreconfigitem(
1280 1280 b'format',
1281 1281 b'use-persistent-nodemap',
1282 1282 default=False,
1283 1283 )
1284 1284 coreconfigitem(
1285 1285 b'format',
1286 1286 b'exp-use-copies-side-data-changeset',
1287 1287 default=False,
1288 1288 experimental=True,
1289 1289 )
1290 1290 coreconfigitem(
1291 1291 b'format',
1292 1292 b'exp-use-side-data',
1293 1293 default=False,
1294 1294 experimental=True,
1295 1295 )
1296 1296 coreconfigitem(
1297 1297 b'format',
1298 1298 b'use-share-safe',
1299 1299 default=False,
1300 1300 )
1301 1301 coreconfigitem(
1302 1302 b'format',
1303 1303 b'internal-phase',
1304 1304 default=False,
1305 1305 experimental=True,
1306 1306 )
1307 1307 coreconfigitem(
1308 1308 b'fsmonitor',
1309 1309 b'warn_when_unused',
1310 1310 default=True,
1311 1311 )
1312 1312 coreconfigitem(
1313 1313 b'fsmonitor',
1314 1314 b'warn_update_file_count',
1315 1315 default=50000,
1316 1316 )
1317 1317 coreconfigitem(
1318 1318 b'fsmonitor',
1319 1319 b'warn_update_file_count_rust',
1320 1320 default=400000,
1321 1321 )
1322 1322 coreconfigitem(
1323 1323 b'help',
1324 1324 br'hidden-command\..*',
1325 1325 default=False,
1326 1326 generic=True,
1327 1327 )
1328 1328 coreconfigitem(
1329 1329 b'help',
1330 1330 br'hidden-topic\..*',
1331 1331 default=False,
1332 1332 generic=True,
1333 1333 )
1334 1334 coreconfigitem(
1335 1335 b'hooks',
1336 b'.*',
1336 b'[^:]*',
1337 1337 default=dynamicdefault,
1338 1338 generic=True,
1339 1339 )
1340 1340 coreconfigitem(
1341 b'hooks',
1342 b'.*:run-with-plain',
1343 default=True,
1344 generic=True,
1345 )
1346 coreconfigitem(
1341 1347 b'hgweb-paths',
1342 1348 b'.*',
1343 1349 default=list,
1344 1350 generic=True,
1345 1351 )
1346 1352 coreconfigitem(
1347 1353 b'hostfingerprints',
1348 1354 b'.*',
1349 1355 default=list,
1350 1356 generic=True,
1351 1357 )
1352 1358 coreconfigitem(
1353 1359 b'hostsecurity',
1354 1360 b'ciphers',
1355 1361 default=None,
1356 1362 )
1357 1363 coreconfigitem(
1358 1364 b'hostsecurity',
1359 1365 b'minimumprotocol',
1360 1366 default=dynamicdefault,
1361 1367 )
1362 1368 coreconfigitem(
1363 1369 b'hostsecurity',
1364 1370 b'.*:minimumprotocol$',
1365 1371 default=dynamicdefault,
1366 1372 generic=True,
1367 1373 )
1368 1374 coreconfigitem(
1369 1375 b'hostsecurity',
1370 1376 b'.*:ciphers$',
1371 1377 default=dynamicdefault,
1372 1378 generic=True,
1373 1379 )
1374 1380 coreconfigitem(
1375 1381 b'hostsecurity',
1376 1382 b'.*:fingerprints$',
1377 1383 default=list,
1378 1384 generic=True,
1379 1385 )
1380 1386 coreconfigitem(
1381 1387 b'hostsecurity',
1382 1388 b'.*:verifycertsfile$',
1383 1389 default=None,
1384 1390 generic=True,
1385 1391 )
1386 1392
1387 1393 coreconfigitem(
1388 1394 b'http_proxy',
1389 1395 b'always',
1390 1396 default=False,
1391 1397 )
1392 1398 coreconfigitem(
1393 1399 b'http_proxy',
1394 1400 b'host',
1395 1401 default=None,
1396 1402 )
1397 1403 coreconfigitem(
1398 1404 b'http_proxy',
1399 1405 b'no',
1400 1406 default=list,
1401 1407 )
1402 1408 coreconfigitem(
1403 1409 b'http_proxy',
1404 1410 b'passwd',
1405 1411 default=None,
1406 1412 )
1407 1413 coreconfigitem(
1408 1414 b'http_proxy',
1409 1415 b'user',
1410 1416 default=None,
1411 1417 )
1412 1418
1413 1419 coreconfigitem(
1414 1420 b'http',
1415 1421 b'timeout',
1416 1422 default=None,
1417 1423 )
1418 1424
1419 1425 coreconfigitem(
1420 1426 b'logtoprocess',
1421 1427 b'commandexception',
1422 1428 default=None,
1423 1429 )
1424 1430 coreconfigitem(
1425 1431 b'logtoprocess',
1426 1432 b'commandfinish',
1427 1433 default=None,
1428 1434 )
1429 1435 coreconfigitem(
1430 1436 b'logtoprocess',
1431 1437 b'command',
1432 1438 default=None,
1433 1439 )
1434 1440 coreconfigitem(
1435 1441 b'logtoprocess',
1436 1442 b'develwarn',
1437 1443 default=None,
1438 1444 )
1439 1445 coreconfigitem(
1440 1446 b'logtoprocess',
1441 1447 b'uiblocked',
1442 1448 default=None,
1443 1449 )
1444 1450 coreconfigitem(
1445 1451 b'merge',
1446 1452 b'checkunknown',
1447 1453 default=b'abort',
1448 1454 )
1449 1455 coreconfigitem(
1450 1456 b'merge',
1451 1457 b'checkignored',
1452 1458 default=b'abort',
1453 1459 )
1454 1460 coreconfigitem(
1455 1461 b'experimental',
1456 1462 b'merge.checkpathconflicts',
1457 1463 default=False,
1458 1464 )
1459 1465 coreconfigitem(
1460 1466 b'merge',
1461 1467 b'followcopies',
1462 1468 default=True,
1463 1469 )
1464 1470 coreconfigitem(
1465 1471 b'merge',
1466 1472 b'on-failure',
1467 1473 default=b'continue',
1468 1474 )
1469 1475 coreconfigitem(
1470 1476 b'merge',
1471 1477 b'preferancestor',
1472 1478 default=lambda: [b'*'],
1473 1479 experimental=True,
1474 1480 )
1475 1481 coreconfigitem(
1476 1482 b'merge',
1477 1483 b'strict-capability-check',
1478 1484 default=False,
1479 1485 )
1480 1486 coreconfigitem(
1481 1487 b'merge-tools',
1482 1488 b'.*',
1483 1489 default=None,
1484 1490 generic=True,
1485 1491 )
1486 1492 coreconfigitem(
1487 1493 b'merge-tools',
1488 1494 br'.*\.args$',
1489 1495 default=b"$local $base $other",
1490 1496 generic=True,
1491 1497 priority=-1,
1492 1498 )
1493 1499 coreconfigitem(
1494 1500 b'merge-tools',
1495 1501 br'.*\.binary$',
1496 1502 default=False,
1497 1503 generic=True,
1498 1504 priority=-1,
1499 1505 )
1500 1506 coreconfigitem(
1501 1507 b'merge-tools',
1502 1508 br'.*\.check$',
1503 1509 default=list,
1504 1510 generic=True,
1505 1511 priority=-1,
1506 1512 )
1507 1513 coreconfigitem(
1508 1514 b'merge-tools',
1509 1515 br'.*\.checkchanged$',
1510 1516 default=False,
1511 1517 generic=True,
1512 1518 priority=-1,
1513 1519 )
1514 1520 coreconfigitem(
1515 1521 b'merge-tools',
1516 1522 br'.*\.executable$',
1517 1523 default=dynamicdefault,
1518 1524 generic=True,
1519 1525 priority=-1,
1520 1526 )
1521 1527 coreconfigitem(
1522 1528 b'merge-tools',
1523 1529 br'.*\.fixeol$',
1524 1530 default=False,
1525 1531 generic=True,
1526 1532 priority=-1,
1527 1533 )
1528 1534 coreconfigitem(
1529 1535 b'merge-tools',
1530 1536 br'.*\.gui$',
1531 1537 default=False,
1532 1538 generic=True,
1533 1539 priority=-1,
1534 1540 )
1535 1541 coreconfigitem(
1536 1542 b'merge-tools',
1537 1543 br'.*\.mergemarkers$',
1538 1544 default=b'basic',
1539 1545 generic=True,
1540 1546 priority=-1,
1541 1547 )
1542 1548 coreconfigitem(
1543 1549 b'merge-tools',
1544 1550 br'.*\.mergemarkertemplate$',
1545 1551 default=dynamicdefault, # take from command-templates.mergemarker
1546 1552 generic=True,
1547 1553 priority=-1,
1548 1554 )
1549 1555 coreconfigitem(
1550 1556 b'merge-tools',
1551 1557 br'.*\.priority$',
1552 1558 default=0,
1553 1559 generic=True,
1554 1560 priority=-1,
1555 1561 )
1556 1562 coreconfigitem(
1557 1563 b'merge-tools',
1558 1564 br'.*\.premerge$',
1559 1565 default=dynamicdefault,
1560 1566 generic=True,
1561 1567 priority=-1,
1562 1568 )
1563 1569 coreconfigitem(
1564 1570 b'merge-tools',
1565 1571 br'.*\.symlink$',
1566 1572 default=False,
1567 1573 generic=True,
1568 1574 priority=-1,
1569 1575 )
1570 1576 coreconfigitem(
1571 1577 b'pager',
1572 1578 b'attend-.*',
1573 1579 default=dynamicdefault,
1574 1580 generic=True,
1575 1581 )
1576 1582 coreconfigitem(
1577 1583 b'pager',
1578 1584 b'ignore',
1579 1585 default=list,
1580 1586 )
1581 1587 coreconfigitem(
1582 1588 b'pager',
1583 1589 b'pager',
1584 1590 default=dynamicdefault,
1585 1591 )
1586 1592 coreconfigitem(
1587 1593 b'patch',
1588 1594 b'eol',
1589 1595 default=b'strict',
1590 1596 )
1591 1597 coreconfigitem(
1592 1598 b'patch',
1593 1599 b'fuzz',
1594 1600 default=2,
1595 1601 )
1596 1602 coreconfigitem(
1597 1603 b'paths',
1598 1604 b'default',
1599 1605 default=None,
1600 1606 )
1601 1607 coreconfigitem(
1602 1608 b'paths',
1603 1609 b'default-push',
1604 1610 default=None,
1605 1611 )
1606 1612 coreconfigitem(
1607 1613 b'paths',
1608 1614 b'.*',
1609 1615 default=None,
1610 1616 generic=True,
1611 1617 )
1612 1618 coreconfigitem(
1613 1619 b'phases',
1614 1620 b'checksubrepos',
1615 1621 default=b'follow',
1616 1622 )
1617 1623 coreconfigitem(
1618 1624 b'phases',
1619 1625 b'new-commit',
1620 1626 default=b'draft',
1621 1627 )
1622 1628 coreconfigitem(
1623 1629 b'phases',
1624 1630 b'publish',
1625 1631 default=True,
1626 1632 )
1627 1633 coreconfigitem(
1628 1634 b'profiling',
1629 1635 b'enabled',
1630 1636 default=False,
1631 1637 )
1632 1638 coreconfigitem(
1633 1639 b'profiling',
1634 1640 b'format',
1635 1641 default=b'text',
1636 1642 )
1637 1643 coreconfigitem(
1638 1644 b'profiling',
1639 1645 b'freq',
1640 1646 default=1000,
1641 1647 )
1642 1648 coreconfigitem(
1643 1649 b'profiling',
1644 1650 b'limit',
1645 1651 default=30,
1646 1652 )
1647 1653 coreconfigitem(
1648 1654 b'profiling',
1649 1655 b'nested',
1650 1656 default=0,
1651 1657 )
1652 1658 coreconfigitem(
1653 1659 b'profiling',
1654 1660 b'output',
1655 1661 default=None,
1656 1662 )
1657 1663 coreconfigitem(
1658 1664 b'profiling',
1659 1665 b'showmax',
1660 1666 default=0.999,
1661 1667 )
1662 1668 coreconfigitem(
1663 1669 b'profiling',
1664 1670 b'showmin',
1665 1671 default=dynamicdefault,
1666 1672 )
1667 1673 coreconfigitem(
1668 1674 b'profiling',
1669 1675 b'showtime',
1670 1676 default=True,
1671 1677 )
1672 1678 coreconfigitem(
1673 1679 b'profiling',
1674 1680 b'sort',
1675 1681 default=b'inlinetime',
1676 1682 )
1677 1683 coreconfigitem(
1678 1684 b'profiling',
1679 1685 b'statformat',
1680 1686 default=b'hotpath',
1681 1687 )
1682 1688 coreconfigitem(
1683 1689 b'profiling',
1684 1690 b'time-track',
1685 1691 default=dynamicdefault,
1686 1692 )
1687 1693 coreconfigitem(
1688 1694 b'profiling',
1689 1695 b'type',
1690 1696 default=b'stat',
1691 1697 )
1692 1698 coreconfigitem(
1693 1699 b'progress',
1694 1700 b'assume-tty',
1695 1701 default=False,
1696 1702 )
1697 1703 coreconfigitem(
1698 1704 b'progress',
1699 1705 b'changedelay',
1700 1706 default=1,
1701 1707 )
1702 1708 coreconfigitem(
1703 1709 b'progress',
1704 1710 b'clear-complete',
1705 1711 default=True,
1706 1712 )
1707 1713 coreconfigitem(
1708 1714 b'progress',
1709 1715 b'debug',
1710 1716 default=False,
1711 1717 )
1712 1718 coreconfigitem(
1713 1719 b'progress',
1714 1720 b'delay',
1715 1721 default=3,
1716 1722 )
1717 1723 coreconfigitem(
1718 1724 b'progress',
1719 1725 b'disable',
1720 1726 default=False,
1721 1727 )
1722 1728 coreconfigitem(
1723 1729 b'progress',
1724 1730 b'estimateinterval',
1725 1731 default=60.0,
1726 1732 )
1727 1733 coreconfigitem(
1728 1734 b'progress',
1729 1735 b'format',
1730 1736 default=lambda: [b'topic', b'bar', b'number', b'estimate'],
1731 1737 )
1732 1738 coreconfigitem(
1733 1739 b'progress',
1734 1740 b'refresh',
1735 1741 default=0.1,
1736 1742 )
1737 1743 coreconfigitem(
1738 1744 b'progress',
1739 1745 b'width',
1740 1746 default=dynamicdefault,
1741 1747 )
1742 1748 coreconfigitem(
1743 1749 b'pull',
1744 1750 b'confirm',
1745 1751 default=False,
1746 1752 )
1747 1753 coreconfigitem(
1748 1754 b'push',
1749 1755 b'pushvars.server',
1750 1756 default=False,
1751 1757 )
1752 1758 coreconfigitem(
1753 1759 b'rewrite',
1754 1760 b'backup-bundle',
1755 1761 default=True,
1756 1762 alias=[(b'ui', b'history-editing-backup')],
1757 1763 )
1758 1764 coreconfigitem(
1759 1765 b'rewrite',
1760 1766 b'update-timestamp',
1761 1767 default=False,
1762 1768 )
1763 1769 coreconfigitem(
1764 1770 b'rewrite',
1765 1771 b'empty-successor',
1766 1772 default=b'skip',
1767 1773 experimental=True,
1768 1774 )
1769 1775 coreconfigitem(
1770 1776 b'storage',
1771 1777 b'new-repo-backend',
1772 1778 default=b'revlogv1',
1773 1779 experimental=True,
1774 1780 )
1775 1781 coreconfigitem(
1776 1782 b'storage',
1777 1783 b'revlog.optimize-delta-parent-choice',
1778 1784 default=True,
1779 1785 alias=[(b'format', b'aggressivemergedeltas')],
1780 1786 )
1781 1787 # experimental as long as rust is experimental (or a C version is implemented)
1782 1788 coreconfigitem(
1783 1789 b'storage',
1784 1790 b'revlog.persistent-nodemap.mmap',
1785 1791 default=True,
1786 1792 )
1787 1793 # experimental as long as format.use-persistent-nodemap is.
1788 1794 coreconfigitem(
1789 1795 b'storage',
1790 1796 b'revlog.persistent-nodemap.slow-path',
1791 1797 default=b"abort",
1792 1798 )
1793 1799
1794 1800 coreconfigitem(
1795 1801 b'storage',
1796 1802 b'revlog.reuse-external-delta',
1797 1803 default=True,
1798 1804 )
1799 1805 coreconfigitem(
1800 1806 b'storage',
1801 1807 b'revlog.reuse-external-delta-parent',
1802 1808 default=None,
1803 1809 )
1804 1810 coreconfigitem(
1805 1811 b'storage',
1806 1812 b'revlog.zlib.level',
1807 1813 default=None,
1808 1814 )
1809 1815 coreconfigitem(
1810 1816 b'storage',
1811 1817 b'revlog.zstd.level',
1812 1818 default=None,
1813 1819 )
1814 1820 coreconfigitem(
1815 1821 b'server',
1816 1822 b'bookmarks-pushkey-compat',
1817 1823 default=True,
1818 1824 )
1819 1825 coreconfigitem(
1820 1826 b'server',
1821 1827 b'bundle1',
1822 1828 default=True,
1823 1829 )
1824 1830 coreconfigitem(
1825 1831 b'server',
1826 1832 b'bundle1gd',
1827 1833 default=None,
1828 1834 )
1829 1835 coreconfigitem(
1830 1836 b'server',
1831 1837 b'bundle1.pull',
1832 1838 default=None,
1833 1839 )
1834 1840 coreconfigitem(
1835 1841 b'server',
1836 1842 b'bundle1gd.pull',
1837 1843 default=None,
1838 1844 )
1839 1845 coreconfigitem(
1840 1846 b'server',
1841 1847 b'bundle1.push',
1842 1848 default=None,
1843 1849 )
1844 1850 coreconfigitem(
1845 1851 b'server',
1846 1852 b'bundle1gd.push',
1847 1853 default=None,
1848 1854 )
1849 1855 coreconfigitem(
1850 1856 b'server',
1851 1857 b'bundle2.stream',
1852 1858 default=True,
1853 1859 alias=[(b'experimental', b'bundle2.stream')],
1854 1860 )
1855 1861 coreconfigitem(
1856 1862 b'server',
1857 1863 b'compressionengines',
1858 1864 default=list,
1859 1865 )
1860 1866 coreconfigitem(
1861 1867 b'server',
1862 1868 b'concurrent-push-mode',
1863 1869 default=b'check-related',
1864 1870 )
1865 1871 coreconfigitem(
1866 1872 b'server',
1867 1873 b'disablefullbundle',
1868 1874 default=False,
1869 1875 )
1870 1876 coreconfigitem(
1871 1877 b'server',
1872 1878 b'maxhttpheaderlen',
1873 1879 default=1024,
1874 1880 )
1875 1881 coreconfigitem(
1876 1882 b'server',
1877 1883 b'pullbundle',
1878 1884 default=False,
1879 1885 )
1880 1886 coreconfigitem(
1881 1887 b'server',
1882 1888 b'preferuncompressed',
1883 1889 default=False,
1884 1890 )
1885 1891 coreconfigitem(
1886 1892 b'server',
1887 1893 b'streamunbundle',
1888 1894 default=False,
1889 1895 )
1890 1896 coreconfigitem(
1891 1897 b'server',
1892 1898 b'uncompressed',
1893 1899 default=True,
1894 1900 )
1895 1901 coreconfigitem(
1896 1902 b'server',
1897 1903 b'uncompressedallowsecret',
1898 1904 default=False,
1899 1905 )
1900 1906 coreconfigitem(
1901 1907 b'server',
1902 1908 b'view',
1903 1909 default=b'served',
1904 1910 )
1905 1911 coreconfigitem(
1906 1912 b'server',
1907 1913 b'validate',
1908 1914 default=False,
1909 1915 )
1910 1916 coreconfigitem(
1911 1917 b'server',
1912 1918 b'zliblevel',
1913 1919 default=-1,
1914 1920 )
1915 1921 coreconfigitem(
1916 1922 b'server',
1917 1923 b'zstdlevel',
1918 1924 default=3,
1919 1925 )
1920 1926 coreconfigitem(
1921 1927 b'share',
1922 1928 b'pool',
1923 1929 default=None,
1924 1930 )
1925 1931 coreconfigitem(
1926 1932 b'share',
1927 1933 b'poolnaming',
1928 1934 default=b'identity',
1929 1935 )
1930 1936 coreconfigitem(
1931 1937 b'share',
1932 1938 b'safe-mismatch.source-not-safe',
1933 1939 default=b'abort',
1934 1940 )
1935 1941 coreconfigitem(
1936 1942 b'share',
1937 1943 b'safe-mismatch.source-safe',
1938 1944 default=b'abort',
1939 1945 )
1940 1946 coreconfigitem(
1941 1947 b'share',
1942 1948 b'safe-mismatch.source-not-safe.warn',
1943 1949 default=True,
1944 1950 )
1945 1951 coreconfigitem(
1946 1952 b'share',
1947 1953 b'safe-mismatch.source-safe.warn',
1948 1954 default=True,
1949 1955 )
1950 1956 coreconfigitem(
1951 1957 b'shelve',
1952 1958 b'maxbackups',
1953 1959 default=10,
1954 1960 )
1955 1961 coreconfigitem(
1956 1962 b'smtp',
1957 1963 b'host',
1958 1964 default=None,
1959 1965 )
1960 1966 coreconfigitem(
1961 1967 b'smtp',
1962 1968 b'local_hostname',
1963 1969 default=None,
1964 1970 )
1965 1971 coreconfigitem(
1966 1972 b'smtp',
1967 1973 b'password',
1968 1974 default=None,
1969 1975 )
1970 1976 coreconfigitem(
1971 1977 b'smtp',
1972 1978 b'port',
1973 1979 default=dynamicdefault,
1974 1980 )
1975 1981 coreconfigitem(
1976 1982 b'smtp',
1977 1983 b'tls',
1978 1984 default=b'none',
1979 1985 )
1980 1986 coreconfigitem(
1981 1987 b'smtp',
1982 1988 b'username',
1983 1989 default=None,
1984 1990 )
1985 1991 coreconfigitem(
1986 1992 b'sparse',
1987 1993 b'missingwarning',
1988 1994 default=True,
1989 1995 experimental=True,
1990 1996 )
1991 1997 coreconfigitem(
1992 1998 b'subrepos',
1993 1999 b'allowed',
1994 2000 default=dynamicdefault, # to make backporting simpler
1995 2001 )
1996 2002 coreconfigitem(
1997 2003 b'subrepos',
1998 2004 b'hg:allowed',
1999 2005 default=dynamicdefault,
2000 2006 )
2001 2007 coreconfigitem(
2002 2008 b'subrepos',
2003 2009 b'git:allowed',
2004 2010 default=dynamicdefault,
2005 2011 )
2006 2012 coreconfigitem(
2007 2013 b'subrepos',
2008 2014 b'svn:allowed',
2009 2015 default=dynamicdefault,
2010 2016 )
2011 2017 coreconfigitem(
2012 2018 b'templates',
2013 2019 b'.*',
2014 2020 default=None,
2015 2021 generic=True,
2016 2022 )
2017 2023 coreconfigitem(
2018 2024 b'templateconfig',
2019 2025 b'.*',
2020 2026 default=dynamicdefault,
2021 2027 generic=True,
2022 2028 )
2023 2029 coreconfigitem(
2024 2030 b'trusted',
2025 2031 b'groups',
2026 2032 default=list,
2027 2033 )
2028 2034 coreconfigitem(
2029 2035 b'trusted',
2030 2036 b'users',
2031 2037 default=list,
2032 2038 )
2033 2039 coreconfigitem(
2034 2040 b'ui',
2035 2041 b'_usedassubrepo',
2036 2042 default=False,
2037 2043 )
2038 2044 coreconfigitem(
2039 2045 b'ui',
2040 2046 b'allowemptycommit',
2041 2047 default=False,
2042 2048 )
2043 2049 coreconfigitem(
2044 2050 b'ui',
2045 2051 b'archivemeta',
2046 2052 default=True,
2047 2053 )
2048 2054 coreconfigitem(
2049 2055 b'ui',
2050 2056 b'askusername',
2051 2057 default=False,
2052 2058 )
2053 2059 coreconfigitem(
2054 2060 b'ui',
2055 2061 b'available-memory',
2056 2062 default=None,
2057 2063 )
2058 2064
2059 2065 coreconfigitem(
2060 2066 b'ui',
2061 2067 b'clonebundlefallback',
2062 2068 default=False,
2063 2069 )
2064 2070 coreconfigitem(
2065 2071 b'ui',
2066 2072 b'clonebundleprefers',
2067 2073 default=list,
2068 2074 )
2069 2075 coreconfigitem(
2070 2076 b'ui',
2071 2077 b'clonebundles',
2072 2078 default=True,
2073 2079 )
2074 2080 coreconfigitem(
2075 2081 b'ui',
2076 2082 b'color',
2077 2083 default=b'auto',
2078 2084 )
2079 2085 coreconfigitem(
2080 2086 b'ui',
2081 2087 b'commitsubrepos',
2082 2088 default=False,
2083 2089 )
2084 2090 coreconfigitem(
2085 2091 b'ui',
2086 2092 b'debug',
2087 2093 default=False,
2088 2094 )
2089 2095 coreconfigitem(
2090 2096 b'ui',
2091 2097 b'debugger',
2092 2098 default=None,
2093 2099 )
2094 2100 coreconfigitem(
2095 2101 b'ui',
2096 2102 b'editor',
2097 2103 default=dynamicdefault,
2098 2104 )
2099 2105 coreconfigitem(
2100 2106 b'ui',
2101 2107 b'detailed-exit-code',
2102 2108 default=False,
2103 2109 experimental=True,
2104 2110 )
2105 2111 coreconfigitem(
2106 2112 b'ui',
2107 2113 b'fallbackencoding',
2108 2114 default=None,
2109 2115 )
2110 2116 coreconfigitem(
2111 2117 b'ui',
2112 2118 b'forcecwd',
2113 2119 default=None,
2114 2120 )
2115 2121 coreconfigitem(
2116 2122 b'ui',
2117 2123 b'forcemerge',
2118 2124 default=None,
2119 2125 )
2120 2126 coreconfigitem(
2121 2127 b'ui',
2122 2128 b'formatdebug',
2123 2129 default=False,
2124 2130 )
2125 2131 coreconfigitem(
2126 2132 b'ui',
2127 2133 b'formatjson',
2128 2134 default=False,
2129 2135 )
2130 2136 coreconfigitem(
2131 2137 b'ui',
2132 2138 b'formatted',
2133 2139 default=None,
2134 2140 )
2135 2141 coreconfigitem(
2136 2142 b'ui',
2137 2143 b'interactive',
2138 2144 default=None,
2139 2145 )
2140 2146 coreconfigitem(
2141 2147 b'ui',
2142 2148 b'interface',
2143 2149 default=None,
2144 2150 )
2145 2151 coreconfigitem(
2146 2152 b'ui',
2147 2153 b'interface.chunkselector',
2148 2154 default=None,
2149 2155 )
2150 2156 coreconfigitem(
2151 2157 b'ui',
2152 2158 b'large-file-limit',
2153 2159 default=10000000,
2154 2160 )
2155 2161 coreconfigitem(
2156 2162 b'ui',
2157 2163 b'logblockedtimes',
2158 2164 default=False,
2159 2165 )
2160 2166 coreconfigitem(
2161 2167 b'ui',
2162 2168 b'merge',
2163 2169 default=None,
2164 2170 )
2165 2171 coreconfigitem(
2166 2172 b'ui',
2167 2173 b'mergemarkers',
2168 2174 default=b'basic',
2169 2175 )
2170 2176 coreconfigitem(
2171 2177 b'ui',
2172 2178 b'message-output',
2173 2179 default=b'stdio',
2174 2180 )
2175 2181 coreconfigitem(
2176 2182 b'ui',
2177 2183 b'nontty',
2178 2184 default=False,
2179 2185 )
2180 2186 coreconfigitem(
2181 2187 b'ui',
2182 2188 b'origbackuppath',
2183 2189 default=None,
2184 2190 )
2185 2191 coreconfigitem(
2186 2192 b'ui',
2187 2193 b'paginate',
2188 2194 default=True,
2189 2195 )
2190 2196 coreconfigitem(
2191 2197 b'ui',
2192 2198 b'patch',
2193 2199 default=None,
2194 2200 )
2195 2201 coreconfigitem(
2196 2202 b'ui',
2197 2203 b'portablefilenames',
2198 2204 default=b'warn',
2199 2205 )
2200 2206 coreconfigitem(
2201 2207 b'ui',
2202 2208 b'promptecho',
2203 2209 default=False,
2204 2210 )
2205 2211 coreconfigitem(
2206 2212 b'ui',
2207 2213 b'quiet',
2208 2214 default=False,
2209 2215 )
2210 2216 coreconfigitem(
2211 2217 b'ui',
2212 2218 b'quietbookmarkmove',
2213 2219 default=False,
2214 2220 )
2215 2221 coreconfigitem(
2216 2222 b'ui',
2217 2223 b'relative-paths',
2218 2224 default=b'legacy',
2219 2225 )
2220 2226 coreconfigitem(
2221 2227 b'ui',
2222 2228 b'remotecmd',
2223 2229 default=b'hg',
2224 2230 )
2225 2231 coreconfigitem(
2226 2232 b'ui',
2227 2233 b'report_untrusted',
2228 2234 default=True,
2229 2235 )
2230 2236 coreconfigitem(
2231 2237 b'ui',
2232 2238 b'rollback',
2233 2239 default=True,
2234 2240 )
2235 2241 coreconfigitem(
2236 2242 b'ui',
2237 2243 b'signal-safe-lock',
2238 2244 default=True,
2239 2245 )
2240 2246 coreconfigitem(
2241 2247 b'ui',
2242 2248 b'slash',
2243 2249 default=False,
2244 2250 )
2245 2251 coreconfigitem(
2246 2252 b'ui',
2247 2253 b'ssh',
2248 2254 default=b'ssh',
2249 2255 )
2250 2256 coreconfigitem(
2251 2257 b'ui',
2252 2258 b'ssherrorhint',
2253 2259 default=None,
2254 2260 )
2255 2261 coreconfigitem(
2256 2262 b'ui',
2257 2263 b'statuscopies',
2258 2264 default=False,
2259 2265 )
2260 2266 coreconfigitem(
2261 2267 b'ui',
2262 2268 b'strict',
2263 2269 default=False,
2264 2270 )
2265 2271 coreconfigitem(
2266 2272 b'ui',
2267 2273 b'style',
2268 2274 default=b'',
2269 2275 )
2270 2276 coreconfigitem(
2271 2277 b'ui',
2272 2278 b'supportcontact',
2273 2279 default=None,
2274 2280 )
2275 2281 coreconfigitem(
2276 2282 b'ui',
2277 2283 b'textwidth',
2278 2284 default=78,
2279 2285 )
2280 2286 coreconfigitem(
2281 2287 b'ui',
2282 2288 b'timeout',
2283 2289 default=b'600',
2284 2290 )
2285 2291 coreconfigitem(
2286 2292 b'ui',
2287 2293 b'timeout.warn',
2288 2294 default=0,
2289 2295 )
2290 2296 coreconfigitem(
2291 2297 b'ui',
2292 2298 b'timestamp-output',
2293 2299 default=False,
2294 2300 )
2295 2301 coreconfigitem(
2296 2302 b'ui',
2297 2303 b'traceback',
2298 2304 default=False,
2299 2305 )
2300 2306 coreconfigitem(
2301 2307 b'ui',
2302 2308 b'tweakdefaults',
2303 2309 default=False,
2304 2310 )
2305 2311 coreconfigitem(b'ui', b'username', alias=[(b'ui', b'user')])
2306 2312 coreconfigitem(
2307 2313 b'ui',
2308 2314 b'verbose',
2309 2315 default=False,
2310 2316 )
2311 2317 coreconfigitem(
2312 2318 b'verify',
2313 2319 b'skipflags',
2314 2320 default=None,
2315 2321 )
2316 2322 coreconfigitem(
2317 2323 b'web',
2318 2324 b'allowbz2',
2319 2325 default=False,
2320 2326 )
2321 2327 coreconfigitem(
2322 2328 b'web',
2323 2329 b'allowgz',
2324 2330 default=False,
2325 2331 )
2326 2332 coreconfigitem(
2327 2333 b'web',
2328 2334 b'allow-pull',
2329 2335 alias=[(b'web', b'allowpull')],
2330 2336 default=True,
2331 2337 )
2332 2338 coreconfigitem(
2333 2339 b'web',
2334 2340 b'allow-push',
2335 2341 alias=[(b'web', b'allow_push')],
2336 2342 default=list,
2337 2343 )
2338 2344 coreconfigitem(
2339 2345 b'web',
2340 2346 b'allowzip',
2341 2347 default=False,
2342 2348 )
2343 2349 coreconfigitem(
2344 2350 b'web',
2345 2351 b'archivesubrepos',
2346 2352 default=False,
2347 2353 )
2348 2354 coreconfigitem(
2349 2355 b'web',
2350 2356 b'cache',
2351 2357 default=True,
2352 2358 )
2353 2359 coreconfigitem(
2354 2360 b'web',
2355 2361 b'comparisoncontext',
2356 2362 default=5,
2357 2363 )
2358 2364 coreconfigitem(
2359 2365 b'web',
2360 2366 b'contact',
2361 2367 default=None,
2362 2368 )
2363 2369 coreconfigitem(
2364 2370 b'web',
2365 2371 b'deny_push',
2366 2372 default=list,
2367 2373 )
2368 2374 coreconfigitem(
2369 2375 b'web',
2370 2376 b'guessmime',
2371 2377 default=False,
2372 2378 )
2373 2379 coreconfigitem(
2374 2380 b'web',
2375 2381 b'hidden',
2376 2382 default=False,
2377 2383 )
2378 2384 coreconfigitem(
2379 2385 b'web',
2380 2386 b'labels',
2381 2387 default=list,
2382 2388 )
2383 2389 coreconfigitem(
2384 2390 b'web',
2385 2391 b'logoimg',
2386 2392 default=b'hglogo.png',
2387 2393 )
2388 2394 coreconfigitem(
2389 2395 b'web',
2390 2396 b'logourl',
2391 2397 default=b'https://mercurial-scm.org/',
2392 2398 )
2393 2399 coreconfigitem(
2394 2400 b'web',
2395 2401 b'accesslog',
2396 2402 default=b'-',
2397 2403 )
2398 2404 coreconfigitem(
2399 2405 b'web',
2400 2406 b'address',
2401 2407 default=b'',
2402 2408 )
2403 2409 coreconfigitem(
2404 2410 b'web',
2405 2411 b'allow-archive',
2406 2412 alias=[(b'web', b'allow_archive')],
2407 2413 default=list,
2408 2414 )
2409 2415 coreconfigitem(
2410 2416 b'web',
2411 2417 b'allow_read',
2412 2418 default=list,
2413 2419 )
2414 2420 coreconfigitem(
2415 2421 b'web',
2416 2422 b'baseurl',
2417 2423 default=None,
2418 2424 )
2419 2425 coreconfigitem(
2420 2426 b'web',
2421 2427 b'cacerts',
2422 2428 default=None,
2423 2429 )
2424 2430 coreconfigitem(
2425 2431 b'web',
2426 2432 b'certificate',
2427 2433 default=None,
2428 2434 )
2429 2435 coreconfigitem(
2430 2436 b'web',
2431 2437 b'collapse',
2432 2438 default=False,
2433 2439 )
2434 2440 coreconfigitem(
2435 2441 b'web',
2436 2442 b'csp',
2437 2443 default=None,
2438 2444 )
2439 2445 coreconfigitem(
2440 2446 b'web',
2441 2447 b'deny_read',
2442 2448 default=list,
2443 2449 )
2444 2450 coreconfigitem(
2445 2451 b'web',
2446 2452 b'descend',
2447 2453 default=True,
2448 2454 )
2449 2455 coreconfigitem(
2450 2456 b'web',
2451 2457 b'description',
2452 2458 default=b"",
2453 2459 )
2454 2460 coreconfigitem(
2455 2461 b'web',
2456 2462 b'encoding',
2457 2463 default=lambda: encoding.encoding,
2458 2464 )
2459 2465 coreconfigitem(
2460 2466 b'web',
2461 2467 b'errorlog',
2462 2468 default=b'-',
2463 2469 )
2464 2470 coreconfigitem(
2465 2471 b'web',
2466 2472 b'ipv6',
2467 2473 default=False,
2468 2474 )
2469 2475 coreconfigitem(
2470 2476 b'web',
2471 2477 b'maxchanges',
2472 2478 default=10,
2473 2479 )
2474 2480 coreconfigitem(
2475 2481 b'web',
2476 2482 b'maxfiles',
2477 2483 default=10,
2478 2484 )
2479 2485 coreconfigitem(
2480 2486 b'web',
2481 2487 b'maxshortchanges',
2482 2488 default=60,
2483 2489 )
2484 2490 coreconfigitem(
2485 2491 b'web',
2486 2492 b'motd',
2487 2493 default=b'',
2488 2494 )
2489 2495 coreconfigitem(
2490 2496 b'web',
2491 2497 b'name',
2492 2498 default=dynamicdefault,
2493 2499 )
2494 2500 coreconfigitem(
2495 2501 b'web',
2496 2502 b'port',
2497 2503 default=8000,
2498 2504 )
2499 2505 coreconfigitem(
2500 2506 b'web',
2501 2507 b'prefix',
2502 2508 default=b'',
2503 2509 )
2504 2510 coreconfigitem(
2505 2511 b'web',
2506 2512 b'push_ssl',
2507 2513 default=True,
2508 2514 )
2509 2515 coreconfigitem(
2510 2516 b'web',
2511 2517 b'refreshinterval',
2512 2518 default=20,
2513 2519 )
2514 2520 coreconfigitem(
2515 2521 b'web',
2516 2522 b'server-header',
2517 2523 default=None,
2518 2524 )
2519 2525 coreconfigitem(
2520 2526 b'web',
2521 2527 b'static',
2522 2528 default=None,
2523 2529 )
2524 2530 coreconfigitem(
2525 2531 b'web',
2526 2532 b'staticurl',
2527 2533 default=None,
2528 2534 )
2529 2535 coreconfigitem(
2530 2536 b'web',
2531 2537 b'stripes',
2532 2538 default=1,
2533 2539 )
2534 2540 coreconfigitem(
2535 2541 b'web',
2536 2542 b'style',
2537 2543 default=b'paper',
2538 2544 )
2539 2545 coreconfigitem(
2540 2546 b'web',
2541 2547 b'templates',
2542 2548 default=None,
2543 2549 )
2544 2550 coreconfigitem(
2545 2551 b'web',
2546 2552 b'view',
2547 2553 default=b'served',
2548 2554 experimental=True,
2549 2555 )
2550 2556 coreconfigitem(
2551 2557 b'worker',
2552 2558 b'backgroundclose',
2553 2559 default=dynamicdefault,
2554 2560 )
2555 2561 # Windows defaults to a limit of 512 open files. A buffer of 128
2556 2562 # should give us enough headway.
2557 2563 coreconfigitem(
2558 2564 b'worker',
2559 2565 b'backgroundclosemaxqueue',
2560 2566 default=384,
2561 2567 )
2562 2568 coreconfigitem(
2563 2569 b'worker',
2564 2570 b'backgroundcloseminfilecount',
2565 2571 default=2048,
2566 2572 )
2567 2573 coreconfigitem(
2568 2574 b'worker',
2569 2575 b'backgroundclosethreadcount',
2570 2576 default=4,
2571 2577 )
2572 2578 coreconfigitem(
2573 2579 b'worker',
2574 2580 b'enabled',
2575 2581 default=True,
2576 2582 )
2577 2583 coreconfigitem(
2578 2584 b'worker',
2579 2585 b'numcpus',
2580 2586 default=None,
2581 2587 )
2582 2588
2583 2589 # Rebase related configuration moved to core because other extension are doing
2584 2590 # strange things. For example, shelve import the extensions to reuse some bit
2585 2591 # without formally loading it.
2586 2592 coreconfigitem(
2587 2593 b'commands',
2588 2594 b'rebase.requiredest',
2589 2595 default=False,
2590 2596 )
2591 2597 coreconfigitem(
2592 2598 b'experimental',
2593 2599 b'rebaseskipobsolete',
2594 2600 default=True,
2595 2601 )
2596 2602 coreconfigitem(
2597 2603 b'rebase',
2598 2604 b'singletransaction',
2599 2605 default=False,
2600 2606 )
2601 2607 coreconfigitem(
2602 2608 b'rebase',
2603 2609 b'experimental.inmemory',
2604 2610 default=False,
2605 2611 )
@@ -1,3066 +1,3073 b''
1 1 The Mercurial system uses a set of configuration files to control
2 2 aspects of its behavior.
3 3
4 4 Troubleshooting
5 5 ===============
6 6
7 7 If you're having problems with your configuration,
8 8 :hg:`config --debug` can help you understand what is introducing
9 9 a setting into your environment.
10 10
11 11 See :hg:`help config.syntax` and :hg:`help config.files`
12 12 for information about how and where to override things.
13 13
14 14 Structure
15 15 =========
16 16
17 17 The configuration files use a simple ini-file format. A configuration
18 18 file consists of sections, led by a ``[section]`` header and followed
19 19 by ``name = value`` entries::
20 20
21 21 [ui]
22 22 username = Firstname Lastname <firstname.lastname@example.net>
23 23 verbose = True
24 24
25 25 The above entries will be referred to as ``ui.username`` and
26 26 ``ui.verbose``, respectively. See :hg:`help config.syntax`.
27 27
28 28 Files
29 29 =====
30 30
31 31 Mercurial reads configuration data from several files, if they exist.
32 32 These files do not exist by default and you will have to create the
33 33 appropriate configuration files yourself:
34 34
35 35 Local configuration is put into the per-repository ``<repo>/.hg/hgrc`` file.
36 36
37 37 Global configuration like the username setting is typically put into:
38 38
39 39 .. container:: windows
40 40
41 41 - ``%USERPROFILE%\mercurial.ini`` (on Windows)
42 42
43 43 .. container:: unix.plan9
44 44
45 45 - ``$HOME/.hgrc`` (on Unix, Plan9)
46 46
47 47 The names of these files depend on the system on which Mercurial is
48 48 installed. ``*.rc`` files from a single directory are read in
49 49 alphabetical order, later ones overriding earlier ones. Where multiple
50 50 paths are given below, settings from earlier paths override later
51 51 ones.
52 52
53 53 .. container:: verbose.unix
54 54
55 55 On Unix, the following files are consulted:
56 56
57 57 - ``<repo>/.hg/hgrc-not-shared`` (per-repository)
58 58 - ``<repo>/.hg/hgrc`` (per-repository)
59 59 - ``$HOME/.hgrc`` (per-user)
60 60 - ``${XDG_CONFIG_HOME:-$HOME/.config}/hg/hgrc`` (per-user)
61 61 - ``<install-root>/etc/mercurial/hgrc`` (per-installation)
62 62 - ``<install-root>/etc/mercurial/hgrc.d/*.rc`` (per-installation)
63 63 - ``/etc/mercurial/hgrc`` (per-system)
64 64 - ``/etc/mercurial/hgrc.d/*.rc`` (per-system)
65 65 - ``<internal>/*.rc`` (defaults)
66 66
67 67 .. container:: verbose.windows
68 68
69 69 On Windows, the following files are consulted:
70 70
71 71 - ``<repo>/.hg/hgrc-not-shared`` (per-repository)
72 72 - ``<repo>/.hg/hgrc`` (per-repository)
73 73 - ``%USERPROFILE%\.hgrc`` (per-user)
74 74 - ``%USERPROFILE%\Mercurial.ini`` (per-user)
75 75 - ``%HOME%\.hgrc`` (per-user)
76 76 - ``%HOME%\Mercurial.ini`` (per-user)
77 77 - ``HKEY_LOCAL_MACHINE\SOFTWARE\Mercurial`` (per-system)
78 78 - ``<install-dir>\hgrc.d\*.rc`` (per-installation)
79 79 - ``<install-dir>\Mercurial.ini`` (per-installation)
80 80 - ``%PROGRAMDATA%\Mercurial\hgrc`` (per-system)
81 81 - ``%PROGRAMDATA%\Mercurial\Mercurial.ini`` (per-system)
82 82 - ``%PROGRAMDATA%\Mercurial\hgrc.d\*.rc`` (per-system)
83 83 - ``<internal>/*.rc`` (defaults)
84 84
85 85 .. note::
86 86
87 87 The registry key ``HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Mercurial``
88 88 is used when running 32-bit Python on 64-bit Windows.
89 89
90 90 .. container:: verbose.plan9
91 91
92 92 On Plan9, the following files are consulted:
93 93
94 94 - ``<repo>/.hg/hgrc-not-shared`` (per-repository)
95 95 - ``<repo>/.hg/hgrc`` (per-repository)
96 96 - ``$home/lib/hgrc`` (per-user)
97 97 - ``<install-root>/lib/mercurial/hgrc`` (per-installation)
98 98 - ``<install-root>/lib/mercurial/hgrc.d/*.rc`` (per-installation)
99 99 - ``/lib/mercurial/hgrc`` (per-system)
100 100 - ``/lib/mercurial/hgrc.d/*.rc`` (per-system)
101 101 - ``<internal>/*.rc`` (defaults)
102 102
103 103 Per-repository configuration options only apply in a
104 104 particular repository. This file is not version-controlled, and
105 105 will not get transferred during a "clone" operation. Options in
106 106 this file override options in all other configuration files.
107 107
108 108 .. container:: unix.plan9
109 109
110 110 On Plan 9 and Unix, most of this file will be ignored if it doesn't
111 111 belong to a trusted user or to a trusted group. See
112 112 :hg:`help config.trusted` for more details.
113 113
114 114 Per-user configuration file(s) are for the user running Mercurial. Options
115 115 in these files apply to all Mercurial commands executed by this user in any
116 116 directory. Options in these files override per-system and per-installation
117 117 options.
118 118
119 119 Per-installation configuration files are searched for in the
120 120 directory where Mercurial is installed. ``<install-root>`` is the
121 121 parent directory of the **hg** executable (or symlink) being run.
122 122
123 123 .. container:: unix.plan9
124 124
125 125 For example, if installed in ``/shared/tools/bin/hg``, Mercurial
126 126 will look in ``/shared/tools/etc/mercurial/hgrc``. Options in these
127 127 files apply to all Mercurial commands executed by any user in any
128 128 directory.
129 129
130 130 Per-installation configuration files are for the system on
131 131 which Mercurial is running. Options in these files apply to all
132 132 Mercurial commands executed by any user in any directory. Registry
133 133 keys contain PATH-like strings, every part of which must reference
134 134 a ``Mercurial.ini`` file or be a directory where ``*.rc`` files will
135 135 be read. Mercurial checks each of these locations in the specified
136 136 order until one or more configuration files are detected.
137 137
138 138 Per-system configuration files are for the system on which Mercurial
139 139 is running. Options in these files apply to all Mercurial commands
140 140 executed by any user in any directory. Options in these files
141 141 override per-installation options.
142 142
143 143 Mercurial comes with some default configuration. The default configuration
144 144 files are installed with Mercurial and will be overwritten on upgrades. Default
145 145 configuration files should never be edited by users or administrators but can
146 146 be overridden in other configuration files. So far the directory only contains
147 147 merge tool configuration but packagers can also put other default configuration
148 148 there.
149 149
150 150 On versions 5.7 and later, if share-safe functionality is enabled,
151 151 shares will read config file of share source too.
152 152 `<share-source/.hg/hgrc>` is read before reading `<repo/.hg/hgrc>`.
153 153
154 154 For configs which should not be shared, `<repo/.hg/hgrc-not-shared>`
155 155 should be used.
156 156
157 157 Syntax
158 158 ======
159 159
160 160 A configuration file consists of sections, led by a ``[section]`` header
161 161 and followed by ``name = value`` entries (sometimes called
162 162 ``configuration keys``)::
163 163
164 164 [spam]
165 165 eggs=ham
166 166 green=
167 167 eggs
168 168
169 169 Each line contains one entry. If the lines that follow are indented,
170 170 they are treated as continuations of that entry. Leading whitespace is
171 171 removed from values. Empty lines are skipped. Lines beginning with
172 172 ``#`` or ``;`` are ignored and may be used to provide comments.
173 173
174 174 Configuration keys can be set multiple times, in which case Mercurial
175 175 will use the value that was configured last. As an example::
176 176
177 177 [spam]
178 178 eggs=large
179 179 ham=serrano
180 180 eggs=small
181 181
182 182 This would set the configuration key named ``eggs`` to ``small``.
183 183
184 184 It is also possible to define a section multiple times. A section can
185 185 be redefined on the same and/or on different configuration files. For
186 186 example::
187 187
188 188 [foo]
189 189 eggs=large
190 190 ham=serrano
191 191 eggs=small
192 192
193 193 [bar]
194 194 eggs=ham
195 195 green=
196 196 eggs
197 197
198 198 [foo]
199 199 ham=prosciutto
200 200 eggs=medium
201 201 bread=toasted
202 202
203 203 This would set the ``eggs``, ``ham``, and ``bread`` configuration keys
204 204 of the ``foo`` section to ``medium``, ``prosciutto``, and ``toasted``,
205 205 respectively. As you can see there only thing that matters is the last
206 206 value that was set for each of the configuration keys.
207 207
208 208 If a configuration key is set multiple times in different
209 209 configuration files the final value will depend on the order in which
210 210 the different configuration files are read, with settings from earlier
211 211 paths overriding later ones as described on the ``Files`` section
212 212 above.
213 213
214 214 A line of the form ``%include file`` will include ``file`` into the
215 215 current configuration file. The inclusion is recursive, which means
216 216 that included files can include other files. Filenames are relative to
217 217 the configuration file in which the ``%include`` directive is found.
218 218 Environment variables and ``~user`` constructs are expanded in
219 219 ``file``. This lets you do something like::
220 220
221 221 %include ~/.hgrc.d/$HOST.rc
222 222
223 223 to include a different configuration file on each computer you use.
224 224
225 225 A line with ``%unset name`` will remove ``name`` from the current
226 226 section, if it has been set previously.
227 227
228 228 The values are either free-form text strings, lists of text strings,
229 229 or Boolean values. Boolean values can be set to true using any of "1",
230 230 "yes", "true", or "on" and to false using "0", "no", "false", or "off"
231 231 (all case insensitive).
232 232
233 233 List values are separated by whitespace or comma, except when values are
234 234 placed in double quotation marks::
235 235
236 236 allow_read = "John Doe, PhD", brian, betty
237 237
238 238 Quotation marks can be escaped by prefixing them with a backslash. Only
239 239 quotation marks at the beginning of a word is counted as a quotation
240 240 (e.g., ``foo"bar baz`` is the list of ``foo"bar`` and ``baz``).
241 241
242 242 Sections
243 243 ========
244 244
245 245 This section describes the different sections that may appear in a
246 246 Mercurial configuration file, the purpose of each section, its possible
247 247 keys, and their possible values.
248 248
249 249 ``alias``
250 250 ---------
251 251
252 252 Defines command aliases.
253 253
254 254 Aliases allow you to define your own commands in terms of other
255 255 commands (or aliases), optionally including arguments. Positional
256 256 arguments in the form of ``$1``, ``$2``, etc. in the alias definition
257 257 are expanded by Mercurial before execution. Positional arguments not
258 258 already used by ``$N`` in the definition are put at the end of the
259 259 command to be executed.
260 260
261 261 Alias definitions consist of lines of the form::
262 262
263 263 <alias> = <command> [<argument>]...
264 264
265 265 For example, this definition::
266 266
267 267 latest = log --limit 5
268 268
269 269 creates a new command ``latest`` that shows only the five most recent
270 270 changesets. You can define subsequent aliases using earlier ones::
271 271
272 272 stable5 = latest -b stable
273 273
274 274 .. note::
275 275
276 276 It is possible to create aliases with the same names as
277 277 existing commands, which will then override the original
278 278 definitions. This is almost always a bad idea!
279 279
280 280 An alias can start with an exclamation point (``!``) to make it a
281 281 shell alias. A shell alias is executed with the shell and will let you
282 282 run arbitrary commands. As an example, ::
283 283
284 284 echo = !echo $@
285 285
286 286 will let you do ``hg echo foo`` to have ``foo`` printed in your
287 287 terminal. A better example might be::
288 288
289 289 purge = !$HG status --no-status --unknown -0 re: | xargs -0 rm -f
290 290
291 291 which will make ``hg purge`` delete all unknown files in the
292 292 repository in the same manner as the purge extension.
293 293
294 294 Positional arguments like ``$1``, ``$2``, etc. in the alias definition
295 295 expand to the command arguments. Unmatched arguments are
296 296 removed. ``$0`` expands to the alias name and ``$@`` expands to all
297 297 arguments separated by a space. ``"$@"`` (with quotes) expands to all
298 298 arguments quoted individually and separated by a space. These expansions
299 299 happen before the command is passed to the shell.
300 300
301 301 Shell aliases are executed in an environment where ``$HG`` expands to
302 302 the path of the Mercurial that was used to execute the alias. This is
303 303 useful when you want to call further Mercurial commands in a shell
304 304 alias, as was done above for the purge alias. In addition,
305 305 ``$HG_ARGS`` expands to the arguments given to Mercurial. In the ``hg
306 306 echo foo`` call above, ``$HG_ARGS`` would expand to ``echo foo``.
307 307
308 308 .. note::
309 309
310 310 Some global configuration options such as ``-R`` are
311 311 processed before shell aliases and will thus not be passed to
312 312 aliases.
313 313
314 314
315 315 ``annotate``
316 316 ------------
317 317
318 318 Settings used when displaying file annotations. All values are
319 319 Booleans and default to False. See :hg:`help config.diff` for
320 320 related options for the diff command.
321 321
322 322 ``ignorews``
323 323 Ignore white space when comparing lines.
324 324
325 325 ``ignorewseol``
326 326 Ignore white space at the end of a line when comparing lines.
327 327
328 328 ``ignorewsamount``
329 329 Ignore changes in the amount of white space.
330 330
331 331 ``ignoreblanklines``
332 332 Ignore changes whose lines are all blank.
333 333
334 334
335 335 ``auth``
336 336 --------
337 337
338 338 Authentication credentials and other authentication-like configuration
339 339 for HTTP connections. This section allows you to store usernames and
340 340 passwords for use when logging *into* HTTP servers. See
341 341 :hg:`help config.web` if you want to configure *who* can login to
342 342 your HTTP server.
343 343
344 344 The following options apply to all hosts.
345 345
346 346 ``cookiefile``
347 347 Path to a file containing HTTP cookie lines. Cookies matching a
348 348 host will be sent automatically.
349 349
350 350 The file format uses the Mozilla cookies.txt format, which defines cookies
351 351 on their own lines. Each line contains 7 fields delimited by the tab
352 352 character (domain, is_domain_cookie, path, is_secure, expires, name,
353 353 value). For more info, do an Internet search for "Netscape cookies.txt
354 354 format."
355 355
356 356 Note: the cookies parser does not handle port numbers on domains. You
357 357 will need to remove ports from the domain for the cookie to be recognized.
358 358 This could result in a cookie being disclosed to an unwanted server.
359 359
360 360 The cookies file is read-only.
361 361
362 362 Other options in this section are grouped by name and have the following
363 363 format::
364 364
365 365 <name>.<argument> = <value>
366 366
367 367 where ``<name>`` is used to group arguments into authentication
368 368 entries. Example::
369 369
370 370 foo.prefix = hg.intevation.de/mercurial
371 371 foo.username = foo
372 372 foo.password = bar
373 373 foo.schemes = http https
374 374
375 375 bar.prefix = secure.example.org
376 376 bar.key = path/to/file.key
377 377 bar.cert = path/to/file.cert
378 378 bar.schemes = https
379 379
380 380 Supported arguments:
381 381
382 382 ``prefix``
383 383 Either ``*`` or a URI prefix with or without the scheme part.
384 384 The authentication entry with the longest matching prefix is used
385 385 (where ``*`` matches everything and counts as a match of length
386 386 1). If the prefix doesn't include a scheme, the match is performed
387 387 against the URI with its scheme stripped as well, and the schemes
388 388 argument, q.v., is then subsequently consulted.
389 389
390 390 ``username``
391 391 Optional. Username to authenticate with. If not given, and the
392 392 remote site requires basic or digest authentication, the user will
393 393 be prompted for it. Environment variables are expanded in the
394 394 username letting you do ``foo.username = $USER``. If the URI
395 395 includes a username, only ``[auth]`` entries with a matching
396 396 username or without a username will be considered.
397 397
398 398 ``password``
399 399 Optional. Password to authenticate with. If not given, and the
400 400 remote site requires basic or digest authentication, the user
401 401 will be prompted for it.
402 402
403 403 ``key``
404 404 Optional. PEM encoded client certificate key file. Environment
405 405 variables are expanded in the filename.
406 406
407 407 ``cert``
408 408 Optional. PEM encoded client certificate chain file. Environment
409 409 variables are expanded in the filename.
410 410
411 411 ``schemes``
412 412 Optional. Space separated list of URI schemes to use this
413 413 authentication entry with. Only used if the prefix doesn't include
414 414 a scheme. Supported schemes are http and https. They will match
415 415 static-http and static-https respectively, as well.
416 416 (default: https)
417 417
418 418 If no suitable authentication entry is found, the user is prompted
419 419 for credentials as usual if required by the remote.
420 420
421 421 ``cmdserver``
422 422 -------------
423 423
424 424 Controls command server settings. (ADVANCED)
425 425
426 426 ``message-encodings``
427 427 List of encodings for the ``m`` (message) channel. The first encoding
428 428 supported by the server will be selected and advertised in the hello
429 429 message. This is useful only when ``ui.message-output`` is set to
430 430 ``channel``. Supported encodings are ``cbor``.
431 431
432 432 ``shutdown-on-interrupt``
433 433 If set to false, the server's main loop will continue running after
434 434 SIGINT received. ``runcommand`` requests can still be interrupted by
435 435 SIGINT. Close the write end of the pipe to shut down the server
436 436 process gracefully.
437 437 (default: True)
438 438
439 439 ``color``
440 440 ---------
441 441
442 442 Configure the Mercurial color mode. For details about how to define your custom
443 443 effect and style see :hg:`help color`.
444 444
445 445 ``mode``
446 446 String: control the method used to output color. One of ``auto``, ``ansi``,
447 447 ``win32``, ``terminfo`` or ``debug``. In auto mode, Mercurial will
448 448 use ANSI mode by default (or win32 mode prior to Windows 10) if it detects a
449 449 terminal. Any invalid value will disable color.
450 450
451 451 ``pagermode``
452 452 String: optional override of ``color.mode`` used with pager.
453 453
454 454 On some systems, terminfo mode may cause problems when using
455 455 color with ``less -R`` as a pager program. less with the -R option
456 456 will only display ECMA-48 color codes, and terminfo mode may sometimes
457 457 emit codes that less doesn't understand. You can work around this by
458 458 either using ansi mode (or auto mode), or by using less -r (which will
459 459 pass through all terminal control codes, not just color control
460 460 codes).
461 461
462 462 On some systems (such as MSYS in Windows), the terminal may support
463 463 a different color mode than the pager program.
464 464
465 465 ``commands``
466 466 ------------
467 467
468 468 ``commit.post-status``
469 469 Show status of files in the working directory after successful commit.
470 470 (default: False)
471 471
472 472 ``merge.require-rev``
473 473 Require that the revision to merge the current commit with be specified on
474 474 the command line. If this is enabled and a revision is not specified, the
475 475 command aborts.
476 476 (default: False)
477 477
478 478 ``push.require-revs``
479 479 Require revisions to push be specified using one or more mechanisms such as
480 480 specifying them positionally on the command line, using ``-r``, ``-b``,
481 481 and/or ``-B`` on the command line, or using ``paths.<path>:pushrev`` in the
482 482 configuration. If this is enabled and revisions are not specified, the
483 483 command aborts.
484 484 (default: False)
485 485
486 486 ``resolve.confirm``
487 487 Confirm before performing action if no filename is passed.
488 488 (default: False)
489 489
490 490 ``resolve.explicit-re-merge``
491 491 Require uses of ``hg resolve`` to specify which action it should perform,
492 492 instead of re-merging files by default.
493 493 (default: False)
494 494
495 495 ``resolve.mark-check``
496 496 Determines what level of checking :hg:`resolve --mark` will perform before
497 497 marking files as resolved. Valid values are ``none`, ``warn``, and
498 498 ``abort``. ``warn`` will output a warning listing the file(s) that still
499 499 have conflict markers in them, but will still mark everything resolved.
500 500 ``abort`` will output the same warning but will not mark things as resolved.
501 501 If --all is passed and this is set to ``abort``, only a warning will be
502 502 shown (an error will not be raised).
503 503 (default: ``none``)
504 504
505 505 ``status.relative``
506 506 Make paths in :hg:`status` output relative to the current directory.
507 507 (default: False)
508 508
509 509 ``status.terse``
510 510 Default value for the --terse flag, which condenses status output.
511 511 (default: empty)
512 512
513 513 ``update.check``
514 514 Determines what level of checking :hg:`update` will perform before moving
515 515 to a destination revision. Valid values are ``abort``, ``none``,
516 516 ``linear``, and ``noconflict``. ``abort`` always fails if the working
517 517 directory has uncommitted changes. ``none`` performs no checking, and may
518 518 result in a merge with uncommitted changes. ``linear`` allows any update
519 519 as long as it follows a straight line in the revision history, and may
520 520 trigger a merge with uncommitted changes. ``noconflict`` will allow any
521 521 update which would not trigger a merge with uncommitted changes, if any
522 522 are present.
523 523 (default: ``linear``)
524 524
525 525 ``update.requiredest``
526 526 Require that the user pass a destination when running :hg:`update`.
527 527 For example, :hg:`update .::` will be allowed, but a plain :hg:`update`
528 528 will be disallowed.
529 529 (default: False)
530 530
531 531 ``committemplate``
532 532 ------------------
533 533
534 534 ``changeset``
535 535 String: configuration in this section is used as the template to
536 536 customize the text shown in the editor when committing.
537 537
538 538 In addition to pre-defined template keywords, commit log specific one
539 539 below can be used for customization:
540 540
541 541 ``extramsg``
542 542 String: Extra message (typically 'Leave message empty to abort
543 543 commit.'). This may be changed by some commands or extensions.
544 544
545 545 For example, the template configuration below shows as same text as
546 546 one shown by default::
547 547
548 548 [committemplate]
549 549 changeset = {desc}\n\n
550 550 HG: Enter commit message. Lines beginning with 'HG:' are removed.
551 551 HG: {extramsg}
552 552 HG: --
553 553 HG: user: {author}\n{ifeq(p2rev, "-1", "",
554 554 "HG: branch merge\n")
555 555 }HG: branch '{branch}'\n{if(activebookmark,
556 556 "HG: bookmark '{activebookmark}'\n") }{subrepos %
557 557 "HG: subrepo {subrepo}\n" }{file_adds %
558 558 "HG: added {file}\n" }{file_mods %
559 559 "HG: changed {file}\n" }{file_dels %
560 560 "HG: removed {file}\n" }{if(files, "",
561 561 "HG: no files changed\n")}
562 562
563 563 ``diff()``
564 564 String: show the diff (see :hg:`help templates` for detail)
565 565
566 566 Sometimes it is helpful to show the diff of the changeset in the editor without
567 567 having to prefix 'HG: ' to each line so that highlighting works correctly. For
568 568 this, Mercurial provides a special string which will ignore everything below
569 569 it::
570 570
571 571 HG: ------------------------ >8 ------------------------
572 572
573 573 For example, the template configuration below will show the diff below the
574 574 extra message::
575 575
576 576 [committemplate]
577 577 changeset = {desc}\n\n
578 578 HG: Enter commit message. Lines beginning with 'HG:' are removed.
579 579 HG: {extramsg}
580 580 HG: ------------------------ >8 ------------------------
581 581 HG: Do not touch the line above.
582 582 HG: Everything below will be removed.
583 583 {diff()}
584 584
585 585 .. note::
586 586
587 587 For some problematic encodings (see :hg:`help win32mbcs` for
588 588 detail), this customization should be configured carefully, to
589 589 avoid showing broken characters.
590 590
591 591 For example, if a multibyte character ending with backslash (0x5c) is
592 592 followed by the ASCII character 'n' in the customized template,
593 593 the sequence of backslash and 'n' is treated as line-feed unexpectedly
594 594 (and the multibyte character is broken, too).
595 595
596 596 Customized template is used for commands below (``--edit`` may be
597 597 required):
598 598
599 599 - :hg:`backout`
600 600 - :hg:`commit`
601 601 - :hg:`fetch` (for merge commit only)
602 602 - :hg:`graft`
603 603 - :hg:`histedit`
604 604 - :hg:`import`
605 605 - :hg:`qfold`, :hg:`qnew` and :hg:`qrefresh`
606 606 - :hg:`rebase`
607 607 - :hg:`shelve`
608 608 - :hg:`sign`
609 609 - :hg:`tag`
610 610 - :hg:`transplant`
611 611
612 612 Configuring items below instead of ``changeset`` allows showing
613 613 customized message only for specific actions, or showing different
614 614 messages for each action.
615 615
616 616 - ``changeset.backout`` for :hg:`backout`
617 617 - ``changeset.commit.amend.merge`` for :hg:`commit --amend` on merges
618 618 - ``changeset.commit.amend.normal`` for :hg:`commit --amend` on other
619 619 - ``changeset.commit.normal.merge`` for :hg:`commit` on merges
620 620 - ``changeset.commit.normal.normal`` for :hg:`commit` on other
621 621 - ``changeset.fetch`` for :hg:`fetch` (impling merge commit)
622 622 - ``changeset.gpg.sign`` for :hg:`sign`
623 623 - ``changeset.graft`` for :hg:`graft`
624 624 - ``changeset.histedit.edit`` for ``edit`` of :hg:`histedit`
625 625 - ``changeset.histedit.fold`` for ``fold`` of :hg:`histedit`
626 626 - ``changeset.histedit.mess`` for ``mess`` of :hg:`histedit`
627 627 - ``changeset.histedit.pick`` for ``pick`` of :hg:`histedit`
628 628 - ``changeset.import.bypass`` for :hg:`import --bypass`
629 629 - ``changeset.import.normal.merge`` for :hg:`import` on merges
630 630 - ``changeset.import.normal.normal`` for :hg:`import` on other
631 631 - ``changeset.mq.qnew`` for :hg:`qnew`
632 632 - ``changeset.mq.qfold`` for :hg:`qfold`
633 633 - ``changeset.mq.qrefresh`` for :hg:`qrefresh`
634 634 - ``changeset.rebase.collapse`` for :hg:`rebase --collapse`
635 635 - ``changeset.rebase.merge`` for :hg:`rebase` on merges
636 636 - ``changeset.rebase.normal`` for :hg:`rebase` on other
637 637 - ``changeset.shelve.shelve`` for :hg:`shelve`
638 638 - ``changeset.tag.add`` for :hg:`tag` without ``--remove``
639 639 - ``changeset.tag.remove`` for :hg:`tag --remove`
640 640 - ``changeset.transplant.merge`` for :hg:`transplant` on merges
641 641 - ``changeset.transplant.normal`` for :hg:`transplant` on other
642 642
643 643 These dot-separated lists of names are treated as hierarchical ones.
644 644 For example, ``changeset.tag.remove`` customizes the commit message
645 645 only for :hg:`tag --remove`, but ``changeset.tag`` customizes the
646 646 commit message for :hg:`tag` regardless of ``--remove`` option.
647 647
648 648 When the external editor is invoked for a commit, the corresponding
649 649 dot-separated list of names without the ``changeset.`` prefix
650 650 (e.g. ``commit.normal.normal``) is in the ``HGEDITFORM`` environment
651 651 variable.
652 652
653 653 In this section, items other than ``changeset`` can be referred from
654 654 others. For example, the configuration to list committed files up
655 655 below can be referred as ``{listupfiles}``::
656 656
657 657 [committemplate]
658 658 listupfiles = {file_adds %
659 659 "HG: added {file}\n" }{file_mods %
660 660 "HG: changed {file}\n" }{file_dels %
661 661 "HG: removed {file}\n" }{if(files, "",
662 662 "HG: no files changed\n")}
663 663
664 664 ``decode/encode``
665 665 -----------------
666 666
667 667 Filters for transforming files on checkout/checkin. This would
668 668 typically be used for newline processing or other
669 669 localization/canonicalization of files.
670 670
671 671 Filters consist of a filter pattern followed by a filter command.
672 672 Filter patterns are globs by default, rooted at the repository root.
673 673 For example, to match any file ending in ``.txt`` in the root
674 674 directory only, use the pattern ``*.txt``. To match any file ending
675 675 in ``.c`` anywhere in the repository, use the pattern ``**.c``.
676 676 For each file only the first matching filter applies.
677 677
678 678 The filter command can start with a specifier, either ``pipe:`` or
679 679 ``tempfile:``. If no specifier is given, ``pipe:`` is used by default.
680 680
681 681 A ``pipe:`` command must accept data on stdin and return the transformed
682 682 data on stdout.
683 683
684 684 Pipe example::
685 685
686 686 [encode]
687 687 # uncompress gzip files on checkin to improve delta compression
688 688 # note: not necessarily a good idea, just an example
689 689 *.gz = pipe: gunzip
690 690
691 691 [decode]
692 692 # recompress gzip files when writing them to the working dir (we
693 693 # can safely omit "pipe:", because it's the default)
694 694 *.gz = gzip
695 695
696 696 A ``tempfile:`` command is a template. The string ``INFILE`` is replaced
697 697 with the name of a temporary file that contains the data to be
698 698 filtered by the command. The string ``OUTFILE`` is replaced with the name
699 699 of an empty temporary file, where the filtered data must be written by
700 700 the command.
701 701
702 702 .. container:: windows
703 703
704 704 .. note::
705 705
706 706 The tempfile mechanism is recommended for Windows systems,
707 707 where the standard shell I/O redirection operators often have
708 708 strange effects and may corrupt the contents of your files.
709 709
710 710 This filter mechanism is used internally by the ``eol`` extension to
711 711 translate line ending characters between Windows (CRLF) and Unix (LF)
712 712 format. We suggest you use the ``eol`` extension for convenience.
713 713
714 714
715 715 ``defaults``
716 716 ------------
717 717
718 718 (defaults are deprecated. Don't use them. Use aliases instead.)
719 719
720 720 Use the ``[defaults]`` section to define command defaults, i.e. the
721 721 default options/arguments to pass to the specified commands.
722 722
723 723 The following example makes :hg:`log` run in verbose mode, and
724 724 :hg:`status` show only the modified files, by default::
725 725
726 726 [defaults]
727 727 log = -v
728 728 status = -m
729 729
730 730 The actual commands, instead of their aliases, must be used when
731 731 defining command defaults. The command defaults will also be applied
732 732 to the aliases of the commands defined.
733 733
734 734
735 735 ``diff``
736 736 --------
737 737
738 738 Settings used when displaying diffs. Everything except for ``unified``
739 739 is a Boolean and defaults to False. See :hg:`help config.annotate`
740 740 for related options for the annotate command.
741 741
742 742 ``git``
743 743 Use git extended diff format.
744 744
745 745 ``nobinary``
746 746 Omit git binary patches.
747 747
748 748 ``nodates``
749 749 Don't include dates in diff headers.
750 750
751 751 ``noprefix``
752 752 Omit 'a/' and 'b/' prefixes from filenames. Ignored in plain mode.
753 753
754 754 ``showfunc``
755 755 Show which function each change is in.
756 756
757 757 ``ignorews``
758 758 Ignore white space when comparing lines.
759 759
760 760 ``ignorewsamount``
761 761 Ignore changes in the amount of white space.
762 762
763 763 ``ignoreblanklines``
764 764 Ignore changes whose lines are all blank.
765 765
766 766 ``unified``
767 767 Number of lines of context to show.
768 768
769 769 ``word-diff``
770 770 Highlight changed words.
771 771
772 772 ``email``
773 773 ---------
774 774
775 775 Settings for extensions that send email messages.
776 776
777 777 ``from``
778 778 Optional. Email address to use in "From" header and SMTP envelope
779 779 of outgoing messages.
780 780
781 781 ``to``
782 782 Optional. Comma-separated list of recipients' email addresses.
783 783
784 784 ``cc``
785 785 Optional. Comma-separated list of carbon copy recipients'
786 786 email addresses.
787 787
788 788 ``bcc``
789 789 Optional. Comma-separated list of blind carbon copy recipients'
790 790 email addresses.
791 791
792 792 ``method``
793 793 Optional. Method to use to send email messages. If value is ``smtp``
794 794 (default), use SMTP (see the ``[smtp]`` section for configuration).
795 795 Otherwise, use as name of program to run that acts like sendmail
796 796 (takes ``-f`` option for sender, list of recipients on command line,
797 797 message on stdin). Normally, setting this to ``sendmail`` or
798 798 ``/usr/sbin/sendmail`` is enough to use sendmail to send messages.
799 799
800 800 ``charsets``
801 801 Optional. Comma-separated list of character sets considered
802 802 convenient for recipients. Addresses, headers, and parts not
803 803 containing patches of outgoing messages will be encoded in the
804 804 first character set to which conversion from local encoding
805 805 (``$HGENCODING``, ``ui.fallbackencoding``) succeeds. If correct
806 806 conversion fails, the text in question is sent as is.
807 807 (default: '')
808 808
809 809 Order of outgoing email character sets:
810 810
811 811 1. ``us-ascii``: always first, regardless of settings
812 812 2. ``email.charsets``: in order given by user
813 813 3. ``ui.fallbackencoding``: if not in email.charsets
814 814 4. ``$HGENCODING``: if not in email.charsets
815 815 5. ``utf-8``: always last, regardless of settings
816 816
817 817 Email example::
818 818
819 819 [email]
820 820 from = Joseph User <joe.user@example.com>
821 821 method = /usr/sbin/sendmail
822 822 # charsets for western Europeans
823 823 # us-ascii, utf-8 omitted, as they are tried first and last
824 824 charsets = iso-8859-1, iso-8859-15, windows-1252
825 825
826 826
827 827 ``extensions``
828 828 --------------
829 829
830 830 Mercurial has an extension mechanism for adding new features. To
831 831 enable an extension, create an entry for it in this section.
832 832
833 833 If you know that the extension is already in Python's search path,
834 834 you can give the name of the module, followed by ``=``, with nothing
835 835 after the ``=``.
836 836
837 837 Otherwise, give a name that you choose, followed by ``=``, followed by
838 838 the path to the ``.py`` file (including the file name extension) that
839 839 defines the extension.
840 840
841 841 To explicitly disable an extension that is enabled in an hgrc of
842 842 broader scope, prepend its path with ``!``, as in ``foo = !/ext/path``
843 843 or ``foo = !`` when path is not supplied.
844 844
845 845 Example for ``~/.hgrc``::
846 846
847 847 [extensions]
848 848 # (the churn extension will get loaded from Mercurial's path)
849 849 churn =
850 850 # (this extension will get loaded from the file specified)
851 851 myfeature = ~/.hgext/myfeature.py
852 852
853 853
854 854 ``format``
855 855 ----------
856 856
857 857 Configuration that controls the repository format. Newer format options are more
858 858 powerful, but incompatible with some older versions of Mercurial. Format options
859 859 are considered at repository initialization only. You need to make a new clone
860 860 for config changes to be taken into account.
861 861
862 862 For more details about repository format and version compatibility, see
863 863 https://www.mercurial-scm.org/wiki/MissingRequirement
864 864
865 865 ``usegeneraldelta``
866 866 Enable or disable the "generaldelta" repository format which improves
867 867 repository compression by allowing "revlog" to store deltas against
868 868 arbitrary revisions instead of the previously stored one. This provides
869 869 significant improvement for repositories with branches.
870 870
871 871 Repositories with this on-disk format require Mercurial version 1.9.
872 872
873 873 Enabled by default.
874 874
875 875 ``dotencode``
876 876 Enable or disable the "dotencode" repository format which enhances
877 877 the "fncache" repository format (which has to be enabled to use
878 878 dotencode) to avoid issues with filenames starting with "._" on
879 879 Mac OS X and spaces on Windows.
880 880
881 881 Repositories with this on-disk format require Mercurial version 1.7.
882 882
883 883 Enabled by default.
884 884
885 885 ``usefncache``
886 886 Enable or disable the "fncache" repository format which enhances
887 887 the "store" repository format (which has to be enabled to use
888 888 fncache) to allow longer filenames and avoids using Windows
889 889 reserved names, e.g. "nul".
890 890
891 891 Repositories with this on-disk format require Mercurial version 1.1.
892 892
893 893 Enabled by default.
894 894
895 895 ``use-persistent-nodemap``
896 896 Enable or disable the "persistent-nodemap" feature which improves
897 897 performance if the rust extensions are available.
898 898
899 899 The "persistence-nodemap" persist the "node -> rev" on disk removing the
900 900 need to dynamically build that mapping for each Mercurial invocation. This
901 901 significantly reduce the startup cost of various local and server-side
902 902 operation for larger repository.
903 903
904 904 The performance improving version of this feature is currently only
905 905 implemented in Rust, so people not using a version of Mercurial compiled
906 906 with the Rust part might actually suffer some slowdown. For this reason,
907 907 Such version will by default refuse to access such repositories. That
908 908 behavior can be controlled by configuration. Check
909 909 :hg:`help config.storage.revlog.persistent-nodemap.slowpath` for details.
910 910
911 911 Repository with this on-disk format require Mercurial version 5.4 or above.
912 912
913 913 Disabled by default.
914 914
915 915 ``use-share-safe``
916 916 Enforce "safe" behaviors for all "shares" that access this repository.
917 917
918 918 With this feature, "shares" using this repository as a source will:
919 919
920 920 * read the source repository's configuration (`<source>/.hg/hgrc`).
921 921 * read and use the source repository's "requirements"
922 922 (except the working copy specific one).
923 923
924 924 Without this feature, "shares" using this repository as a source will:
925 925
926 926 * keep tracking the repository "requirements" in the share only, ignoring
927 927 the source "requirements", possibly diverging from them.
928 928 * ignore source repository config. This can create problems, like silently
929 929 ignoring important hooks.
930 930
931 931 Beware that existing shares will not be upgraded/downgraded, and by
932 932 default, Mercurial will refuse to interact with them until the mismatch
933 933 is resolved. See :hg:`help config share.safe-mismatch.source-safe` and
934 934 :hg:`help config share.safe-mismatch.source-not-safe` for details.
935 935
936 936 Introduced in Mercurial 5.7.
937 937
938 938 Disabled by default.
939 939
940 940 ``usestore``
941 941 Enable or disable the "store" repository format which improves
942 942 compatibility with systems that fold case or otherwise mangle
943 943 filenames. Disabling this option will allow you to store longer filenames
944 944 in some situations at the expense of compatibility.
945 945
946 946 Repositories with this on-disk format require Mercurial version 0.9.4.
947 947
948 948 Enabled by default.
949 949
950 950 ``sparse-revlog``
951 951 Enable or disable the ``sparse-revlog`` delta strategy. This format improves
952 952 delta re-use inside revlog. For very branchy repositories, it results in a
953 953 smaller store. For repositories with many revisions, it also helps
954 954 performance (by using shortened delta chains.)
955 955
956 956 Repositories with this on-disk format require Mercurial version 4.7
957 957
958 958 Enabled by default.
959 959
960 960 ``revlog-compression``
961 961 Compression algorithm used by revlog. Supported values are `zlib` and
962 962 `zstd`. The `zlib` engine is the historical default of Mercurial. `zstd` is
963 963 a newer format that is usually a net win over `zlib`, operating faster at
964 964 better compression rates. Use `zstd` to reduce CPU usage. Multiple values
965 965 can be specified, the first available one will be used.
966 966
967 967 On some systems, the Mercurial installation may lack `zstd` support.
968 968
969 969 Default is `zlib`.
970 970
971 971 ``bookmarks-in-store``
972 972 Store bookmarks in .hg/store/. This means that bookmarks are shared when
973 973 using `hg share` regardless of the `-B` option.
974 974
975 975 Repositories with this on-disk format require Mercurial version 5.1.
976 976
977 977 Disabled by default.
978 978
979 979
980 980 ``graph``
981 981 ---------
982 982
983 983 Web graph view configuration. This section let you change graph
984 984 elements display properties by branches, for instance to make the
985 985 ``default`` branch stand out.
986 986
987 987 Each line has the following format::
988 988
989 989 <branch>.<argument> = <value>
990 990
991 991 where ``<branch>`` is the name of the branch being
992 992 customized. Example::
993 993
994 994 [graph]
995 995 # 2px width
996 996 default.width = 2
997 997 # red color
998 998 default.color = FF0000
999 999
1000 1000 Supported arguments:
1001 1001
1002 1002 ``width``
1003 1003 Set branch edges width in pixels.
1004 1004
1005 1005 ``color``
1006 1006 Set branch edges color in hexadecimal RGB notation.
1007 1007
1008 1008 ``hooks``
1009 1009 ---------
1010 1010
1011 1011 Commands or Python functions that get automatically executed by
1012 1012 various actions such as starting or finishing a commit. Multiple
1013 1013 hooks can be run for the same action by appending a suffix to the
1014 1014 action. Overriding a site-wide hook can be done by changing its
1015 1015 value or setting it to an empty string. Hooks can be prioritized
1016 1016 by adding a prefix of ``priority.`` to the hook name on a new line
1017 1017 and setting the priority. The default priority is 0.
1018 1018
1019 1019 Example ``.hg/hgrc``::
1020 1020
1021 1021 [hooks]
1022 1022 # update working directory after adding changesets
1023 1023 changegroup.update = hg update
1024 1024 # do not use the site-wide hook
1025 1025 incoming =
1026 1026 incoming.email = /my/email/hook
1027 1027 incoming.autobuild = /my/build/hook
1028 1028 # force autobuild hook to run before other incoming hooks
1029 1029 priority.incoming.autobuild = 1
1030 ### control HGPLAIN setting when running autobuild hook
1031 # HGPLAIN always set (default from Mercurial 5.7)
1032 incoming.autobuild:run-with-plain = yes
1033 # HGPLAIN never set
1034 incoming.autobuild:run-with-plain = no
1035 # HGPLAIN inherited from environment (default before Mercurila 5.7)
1036 incoming.autobuild:run-with-plain = auto
1030 1037
1031 1038 Most hooks are run with environment variables set that give useful
1032 1039 additional information. For each hook below, the environment variables
1033 1040 it is passed are listed with names in the form ``$HG_foo``. The
1034 1041 ``$HG_HOOKTYPE`` and ``$HG_HOOKNAME`` variables are set for all hooks.
1035 1042 They contain the type of hook which triggered the run and the full name
1036 1043 of the hook in the config, respectively. In the example above, this will
1037 1044 be ``$HG_HOOKTYPE=incoming`` and ``$HG_HOOKNAME=incoming.email``.
1038 1045
1039 1046 .. container:: windows
1040 1047
1041 1048 Some basic Unix syntax can be enabled for portability, including ``$VAR``
1042 1049 and ``${VAR}`` style variables. A ``~`` followed by ``\`` or ``/`` will
1043 1050 be expanded to ``%USERPROFILE%`` to simulate a subset of tilde expansion
1044 1051 on Unix. To use a literal ``$`` or ``~``, it must be escaped with a back
1045 1052 slash or inside of a strong quote. Strong quotes will be replaced by
1046 1053 double quotes after processing.
1047 1054
1048 1055 This feature is enabled by adding a prefix of ``tonative.`` to the hook
1049 1056 name on a new line, and setting it to ``True``. For example::
1050 1057
1051 1058 [hooks]
1052 1059 incoming.autobuild = /my/build/hook
1053 1060 # enable translation to cmd.exe syntax for autobuild hook
1054 1061 tonative.incoming.autobuild = True
1055 1062
1056 1063 ``changegroup``
1057 1064 Run after a changegroup has been added via push, pull or unbundle. The ID of
1058 1065 the first new changeset is in ``$HG_NODE`` and last is in ``$HG_NODE_LAST``.
1059 1066 The URL from which changes came is in ``$HG_URL``.
1060 1067
1061 1068 ``commit``
1062 1069 Run after a changeset has been created in the local repository. The ID
1063 1070 of the newly created changeset is in ``$HG_NODE``. Parent changeset
1064 1071 IDs are in ``$HG_PARENT1`` and ``$HG_PARENT2``.
1065 1072
1066 1073 ``incoming``
1067 1074 Run after a changeset has been pulled, pushed, or unbundled into
1068 1075 the local repository. The ID of the newly arrived changeset is in
1069 1076 ``$HG_NODE``. The URL that was source of the changes is in ``$HG_URL``.
1070 1077
1071 1078 ``outgoing``
1072 1079 Run after sending changes from the local repository to another. The ID of
1073 1080 first changeset sent is in ``$HG_NODE``. The source of operation is in
1074 1081 ``$HG_SOURCE``. Also see :hg:`help config.hooks.preoutgoing`.
1075 1082
1076 1083 ``post-<command>``
1077 1084 Run after successful invocations of the associated command. The
1078 1085 contents of the command line are passed as ``$HG_ARGS`` and the result
1079 1086 code in ``$HG_RESULT``. Parsed command line arguments are passed as
1080 1087 ``$HG_PATS`` and ``$HG_OPTS``. These contain string representations of
1081 1088 the python data internally passed to <command>. ``$HG_OPTS`` is a
1082 1089 dictionary of options (with unspecified options set to their defaults).
1083 1090 ``$HG_PATS`` is a list of arguments. Hook failure is ignored.
1084 1091
1085 1092 ``fail-<command>``
1086 1093 Run after a failed invocation of an associated command. The contents
1087 1094 of the command line are passed as ``$HG_ARGS``. Parsed command line
1088 1095 arguments are passed as ``$HG_PATS`` and ``$HG_OPTS``. These contain
1089 1096 string representations of the python data internally passed to
1090 1097 <command>. ``$HG_OPTS`` is a dictionary of options (with unspecified
1091 1098 options set to their defaults). ``$HG_PATS`` is a list of arguments.
1092 1099 Hook failure is ignored.
1093 1100
1094 1101 ``pre-<command>``
1095 1102 Run before executing the associated command. The contents of the
1096 1103 command line are passed as ``$HG_ARGS``. Parsed command line arguments
1097 1104 are passed as ``$HG_PATS`` and ``$HG_OPTS``. These contain string
1098 1105 representations of the data internally passed to <command>. ``$HG_OPTS``
1099 1106 is a dictionary of options (with unspecified options set to their
1100 1107 defaults). ``$HG_PATS`` is a list of arguments. If the hook returns
1101 1108 failure, the command doesn't execute and Mercurial returns the failure
1102 1109 code.
1103 1110
1104 1111 ``prechangegroup``
1105 1112 Run before a changegroup is added via push, pull or unbundle. Exit
1106 1113 status 0 allows the changegroup to proceed. A non-zero status will
1107 1114 cause the push, pull or unbundle to fail. The URL from which changes
1108 1115 will come is in ``$HG_URL``.
1109 1116
1110 1117 ``precommit``
1111 1118 Run before starting a local commit. Exit status 0 allows the
1112 1119 commit to proceed. A non-zero status will cause the commit to fail.
1113 1120 Parent changeset IDs are in ``$HG_PARENT1`` and ``$HG_PARENT2``.
1114 1121
1115 1122 ``prelistkeys``
1116 1123 Run before listing pushkeys (like bookmarks) in the
1117 1124 repository. A non-zero status will cause failure. The key namespace is
1118 1125 in ``$HG_NAMESPACE``.
1119 1126
1120 1127 ``preoutgoing``
1121 1128 Run before collecting changes to send from the local repository to
1122 1129 another. A non-zero status will cause failure. This lets you prevent
1123 1130 pull over HTTP or SSH. It can also prevent propagating commits (via
1124 1131 local pull, push (outbound) or bundle commands), but not completely,
1125 1132 since you can just copy files instead. The source of operation is in
1126 1133 ``$HG_SOURCE``. If "serve", the operation is happening on behalf of a remote
1127 1134 SSH or HTTP repository. If "push", "pull" or "bundle", the operation
1128 1135 is happening on behalf of a repository on same system.
1129 1136
1130 1137 ``prepushkey``
1131 1138 Run before a pushkey (like a bookmark) is added to the
1132 1139 repository. A non-zero status will cause the key to be rejected. The
1133 1140 key namespace is in ``$HG_NAMESPACE``, the key is in ``$HG_KEY``,
1134 1141 the old value (if any) is in ``$HG_OLD``, and the new value is in
1135 1142 ``$HG_NEW``.
1136 1143
1137 1144 ``pretag``
1138 1145 Run before creating a tag. Exit status 0 allows the tag to be
1139 1146 created. A non-zero status will cause the tag to fail. The ID of the
1140 1147 changeset to tag is in ``$HG_NODE``. The name of tag is in ``$HG_TAG``. The
1141 1148 tag is local if ``$HG_LOCAL=1``, or in the repository if ``$HG_LOCAL=0``.
1142 1149
1143 1150 ``pretxnopen``
1144 1151 Run before any new repository transaction is open. The reason for the
1145 1152 transaction will be in ``$HG_TXNNAME``, and a unique identifier for the
1146 1153 transaction will be in ``HG_TXNID``. A non-zero status will prevent the
1147 1154 transaction from being opened.
1148 1155
1149 1156 ``pretxnclose``
1150 1157 Run right before the transaction is actually finalized. Any repository change
1151 1158 will be visible to the hook program. This lets you validate the transaction
1152 1159 content or change it. Exit status 0 allows the commit to proceed. A non-zero
1153 1160 status will cause the transaction to be rolled back. The reason for the
1154 1161 transaction opening will be in ``$HG_TXNNAME``, and a unique identifier for
1155 1162 the transaction will be in ``HG_TXNID``. The rest of the available data will
1156 1163 vary according the transaction type. New changesets will add ``$HG_NODE``
1157 1164 (the ID of the first added changeset), ``$HG_NODE_LAST`` (the ID of the last
1158 1165 added changeset), ``$HG_URL`` and ``$HG_SOURCE`` variables. Bookmark and
1159 1166 phase changes will set ``HG_BOOKMARK_MOVED`` and ``HG_PHASES_MOVED`` to ``1``
1160 1167 respectively, etc.
1161 1168
1162 1169 ``pretxnclose-bookmark``
1163 1170 Run right before a bookmark change is actually finalized. Any repository
1164 1171 change will be visible to the hook program. This lets you validate the
1165 1172 transaction content or change it. Exit status 0 allows the commit to
1166 1173 proceed. A non-zero status will cause the transaction to be rolled back.
1167 1174 The name of the bookmark will be available in ``$HG_BOOKMARK``, the new
1168 1175 bookmark location will be available in ``$HG_NODE`` while the previous
1169 1176 location will be available in ``$HG_OLDNODE``. In case of a bookmark
1170 1177 creation ``$HG_OLDNODE`` will be empty. In case of deletion ``$HG_NODE``
1171 1178 will be empty.
1172 1179 In addition, the reason for the transaction opening will be in
1173 1180 ``$HG_TXNNAME``, and a unique identifier for the transaction will be in
1174 1181 ``HG_TXNID``.
1175 1182
1176 1183 ``pretxnclose-phase``
1177 1184 Run right before a phase change is actually finalized. Any repository change
1178 1185 will be visible to the hook program. This lets you validate the transaction
1179 1186 content or change it. Exit status 0 allows the commit to proceed. A non-zero
1180 1187 status will cause the transaction to be rolled back. The hook is called
1181 1188 multiple times, once for each revision affected by a phase change.
1182 1189 The affected node is available in ``$HG_NODE``, the phase in ``$HG_PHASE``
1183 1190 while the previous ``$HG_OLDPHASE``. In case of new node, ``$HG_OLDPHASE``
1184 1191 will be empty. In addition, the reason for the transaction opening will be in
1185 1192 ``$HG_TXNNAME``, and a unique identifier for the transaction will be in
1186 1193 ``HG_TXNID``. The hook is also run for newly added revisions. In this case
1187 1194 the ``$HG_OLDPHASE`` entry will be empty.
1188 1195
1189 1196 ``txnclose``
1190 1197 Run after any repository transaction has been committed. At this
1191 1198 point, the transaction can no longer be rolled back. The hook will run
1192 1199 after the lock is released. See :hg:`help config.hooks.pretxnclose` for
1193 1200 details about available variables.
1194 1201
1195 1202 ``txnclose-bookmark``
1196 1203 Run after any bookmark change has been committed. At this point, the
1197 1204 transaction can no longer be rolled back. The hook will run after the lock
1198 1205 is released. See :hg:`help config.hooks.pretxnclose-bookmark` for details
1199 1206 about available variables.
1200 1207
1201 1208 ``txnclose-phase``
1202 1209 Run after any phase change has been committed. At this point, the
1203 1210 transaction can no longer be rolled back. The hook will run after the lock
1204 1211 is released. See :hg:`help config.hooks.pretxnclose-phase` for details about
1205 1212 available variables.
1206 1213
1207 1214 ``txnabort``
1208 1215 Run when a transaction is aborted. See :hg:`help config.hooks.pretxnclose`
1209 1216 for details about available variables.
1210 1217
1211 1218 ``pretxnchangegroup``
1212 1219 Run after a changegroup has been added via push, pull or unbundle, but before
1213 1220 the transaction has been committed. The changegroup is visible to the hook
1214 1221 program. This allows validation of incoming changes before accepting them.
1215 1222 The ID of the first new changeset is in ``$HG_NODE`` and last is in
1216 1223 ``$HG_NODE_LAST``. Exit status 0 allows the transaction to commit. A non-zero
1217 1224 status will cause the transaction to be rolled back, and the push, pull or
1218 1225 unbundle will fail. The URL that was the source of changes is in ``$HG_URL``.
1219 1226
1220 1227 ``pretxncommit``
1221 1228 Run after a changeset has been created, but before the transaction is
1222 1229 committed. The changeset is visible to the hook program. This allows
1223 1230 validation of the commit message and changes. Exit status 0 allows the
1224 1231 commit to proceed. A non-zero status will cause the transaction to
1225 1232 be rolled back. The ID of the new changeset is in ``$HG_NODE``. The parent
1226 1233 changeset IDs are in ``$HG_PARENT1`` and ``$HG_PARENT2``.
1227 1234
1228 1235 ``preupdate``
1229 1236 Run before updating the working directory. Exit status 0 allows
1230 1237 the update to proceed. A non-zero status will prevent the update.
1231 1238 The changeset ID of first new parent is in ``$HG_PARENT1``. If updating to a
1232 1239 merge, the ID of second new parent is in ``$HG_PARENT2``.
1233 1240
1234 1241 ``listkeys``
1235 1242 Run after listing pushkeys (like bookmarks) in the repository. The
1236 1243 key namespace is in ``$HG_NAMESPACE``. ``$HG_VALUES`` is a
1237 1244 dictionary containing the keys and values.
1238 1245
1239 1246 ``pushkey``
1240 1247 Run after a pushkey (like a bookmark) is added to the
1241 1248 repository. The key namespace is in ``$HG_NAMESPACE``, the key is in
1242 1249 ``$HG_KEY``, the old value (if any) is in ``$HG_OLD``, and the new
1243 1250 value is in ``$HG_NEW``.
1244 1251
1245 1252 ``tag``
1246 1253 Run after a tag is created. The ID of the tagged changeset is in ``$HG_NODE``.
1247 1254 The name of tag is in ``$HG_TAG``. The tag is local if ``$HG_LOCAL=1``, or in
1248 1255 the repository if ``$HG_LOCAL=0``.
1249 1256
1250 1257 ``update``
1251 1258 Run after updating the working directory. The changeset ID of first
1252 1259 new parent is in ``$HG_PARENT1``. If updating to a merge, the ID of second new
1253 1260 parent is in ``$HG_PARENT2``. If the update succeeded, ``$HG_ERROR=0``. If the
1254 1261 update failed (e.g. because conflicts were not resolved), ``$HG_ERROR=1``.
1255 1262
1256 1263 .. note::
1257 1264
1258 1265 It is generally better to use standard hooks rather than the
1259 1266 generic pre- and post- command hooks, as they are guaranteed to be
1260 1267 called in the appropriate contexts for influencing transactions.
1261 1268 Also, hooks like "commit" will be called in all contexts that
1262 1269 generate a commit (e.g. tag) and not just the commit command.
1263 1270
1264 1271 .. note::
1265 1272
1266 1273 Environment variables with empty values may not be passed to
1267 1274 hooks on platforms such as Windows. As an example, ``$HG_PARENT2``
1268 1275 will have an empty value under Unix-like platforms for non-merge
1269 1276 changesets, while it will not be available at all under Windows.
1270 1277
1271 1278 The syntax for Python hooks is as follows::
1272 1279
1273 1280 hookname = python:modulename.submodule.callable
1274 1281 hookname = python:/path/to/python/module.py:callable
1275 1282
1276 1283 Python hooks are run within the Mercurial process. Each hook is
1277 1284 called with at least three keyword arguments: a ui object (keyword
1278 1285 ``ui``), a repository object (keyword ``repo``), and a ``hooktype``
1279 1286 keyword that tells what kind of hook is used. Arguments listed as
1280 1287 environment variables above are passed as keyword arguments, with no
1281 1288 ``HG_`` prefix, and names in lower case.
1282 1289
1283 1290 If a Python hook returns a "true" value or raises an exception, this
1284 1291 is treated as a failure.
1285 1292
1286 1293
1287 1294 ``hostfingerprints``
1288 1295 --------------------
1289 1296
1290 1297 (Deprecated. Use ``[hostsecurity]``'s ``fingerprints`` options instead.)
1291 1298
1292 1299 Fingerprints of the certificates of known HTTPS servers.
1293 1300
1294 1301 A HTTPS connection to a server with a fingerprint configured here will
1295 1302 only succeed if the servers certificate matches the fingerprint.
1296 1303 This is very similar to how ssh known hosts works.
1297 1304
1298 1305 The fingerprint is the SHA-1 hash value of the DER encoded certificate.
1299 1306 Multiple values can be specified (separated by spaces or commas). This can
1300 1307 be used to define both old and new fingerprints while a host transitions
1301 1308 to a new certificate.
1302 1309
1303 1310 The CA chain and web.cacerts is not used for servers with a fingerprint.
1304 1311
1305 1312 For example::
1306 1313
1307 1314 [hostfingerprints]
1308 1315 hg.intevation.de = fc:e2:8d:d9:51:cd:cb:c1:4d:18:6b:b7:44:8d:49:72:57:e6:cd:33
1309 1316 hg.intevation.org = fc:e2:8d:d9:51:cd:cb:c1:4d:18:6b:b7:44:8d:49:72:57:e6:cd:33
1310 1317
1311 1318 ``hostsecurity``
1312 1319 ----------------
1313 1320
1314 1321 Used to specify global and per-host security settings for connecting to
1315 1322 other machines.
1316 1323
1317 1324 The following options control default behavior for all hosts.
1318 1325
1319 1326 ``ciphers``
1320 1327 Defines the cryptographic ciphers to use for connections.
1321 1328
1322 1329 Value must be a valid OpenSSL Cipher List Format as documented at
1323 1330 https://www.openssl.org/docs/manmaster/apps/ciphers.html#CIPHER-LIST-FORMAT.
1324 1331
1325 1332 This setting is for advanced users only. Setting to incorrect values
1326 1333 can significantly lower connection security or decrease performance.
1327 1334 You have been warned.
1328 1335
1329 1336 This option requires Python 2.7.
1330 1337
1331 1338 ``minimumprotocol``
1332 1339 Defines the minimum channel encryption protocol to use.
1333 1340
1334 1341 By default, the highest version of TLS supported by both client and server
1335 1342 is used.
1336 1343
1337 1344 Allowed values are: ``tls1.0``, ``tls1.1``, ``tls1.2``.
1338 1345
1339 1346 When running on an old Python version, only ``tls1.0`` is allowed since
1340 1347 old versions of Python only support up to TLS 1.0.
1341 1348
1342 1349 When running a Python that supports modern TLS versions, the default is
1343 1350 ``tls1.1``. ``tls1.0`` can still be used to allow TLS 1.0. However, this
1344 1351 weakens security and should only be used as a feature of last resort if
1345 1352 a server does not support TLS 1.1+.
1346 1353
1347 1354 Options in the ``[hostsecurity]`` section can have the form
1348 1355 ``hostname``:``setting``. This allows multiple settings to be defined on a
1349 1356 per-host basis.
1350 1357
1351 1358 The following per-host settings can be defined.
1352 1359
1353 1360 ``ciphers``
1354 1361 This behaves like ``ciphers`` as described above except it only applies
1355 1362 to the host on which it is defined.
1356 1363
1357 1364 ``fingerprints``
1358 1365 A list of hashes of the DER encoded peer/remote certificate. Values have
1359 1366 the form ``algorithm``:``fingerprint``. e.g.
1360 1367 ``sha256:c3ab8ff13720e8ad9047dd39466b3c8974e592c2fa383d4a3960714caef0c4f2``.
1361 1368 In addition, colons (``:``) can appear in the fingerprint part.
1362 1369
1363 1370 The following algorithms/prefixes are supported: ``sha1``, ``sha256``,
1364 1371 ``sha512``.
1365 1372
1366 1373 Use of ``sha256`` or ``sha512`` is preferred.
1367 1374
1368 1375 If a fingerprint is specified, the CA chain is not validated for this
1369 1376 host and Mercurial will require the remote certificate to match one
1370 1377 of the fingerprints specified. This means if the server updates its
1371 1378 certificate, Mercurial will abort until a new fingerprint is defined.
1372 1379 This can provide stronger security than traditional CA-based validation
1373 1380 at the expense of convenience.
1374 1381
1375 1382 This option takes precedence over ``verifycertsfile``.
1376 1383
1377 1384 ``minimumprotocol``
1378 1385 This behaves like ``minimumprotocol`` as described above except it
1379 1386 only applies to the host on which it is defined.
1380 1387
1381 1388 ``verifycertsfile``
1382 1389 Path to file a containing a list of PEM encoded certificates used to
1383 1390 verify the server certificate. Environment variables and ``~user``
1384 1391 constructs are expanded in the filename.
1385 1392
1386 1393 The server certificate or the certificate's certificate authority (CA)
1387 1394 must match a certificate from this file or certificate verification
1388 1395 will fail and connections to the server will be refused.
1389 1396
1390 1397 If defined, only certificates provided by this file will be used:
1391 1398 ``web.cacerts`` and any system/default certificates will not be
1392 1399 used.
1393 1400
1394 1401 This option has no effect if the per-host ``fingerprints`` option
1395 1402 is set.
1396 1403
1397 1404 The format of the file is as follows::
1398 1405
1399 1406 -----BEGIN CERTIFICATE-----
1400 1407 ... (certificate in base64 PEM encoding) ...
1401 1408 -----END CERTIFICATE-----
1402 1409 -----BEGIN CERTIFICATE-----
1403 1410 ... (certificate in base64 PEM encoding) ...
1404 1411 -----END CERTIFICATE-----
1405 1412
1406 1413 For example::
1407 1414
1408 1415 [hostsecurity]
1409 1416 hg.example.com:fingerprints = sha256:c3ab8ff13720e8ad9047dd39466b3c8974e592c2fa383d4a3960714caef0c4f2
1410 1417 hg2.example.com:fingerprints = sha1:914f1aff87249c09b6859b88b1906d30756491ca, sha1:fc:e2:8d:d9:51:cd:cb:c1:4d:18:6b:b7:44:8d:49:72:57:e6:cd:33
1411 1418 hg3.example.com:fingerprints = sha256:9a:b0:dc:e2:75:ad:8a:b7:84:58:e5:1f:07:32:f1:87:e6:bd:24:22:af:b7:ce:8e:9c:b4:10:cf:b9:f4:0e:d2
1412 1419 foo.example.com:verifycertsfile = /etc/ssl/trusted-ca-certs.pem
1413 1420
1414 1421 To change the default minimum protocol version to TLS 1.2 but to allow TLS 1.1
1415 1422 when connecting to ``hg.example.com``::
1416 1423
1417 1424 [hostsecurity]
1418 1425 minimumprotocol = tls1.2
1419 1426 hg.example.com:minimumprotocol = tls1.1
1420 1427
1421 1428 ``http_proxy``
1422 1429 --------------
1423 1430
1424 1431 Used to access web-based Mercurial repositories through a HTTP
1425 1432 proxy.
1426 1433
1427 1434 ``host``
1428 1435 Host name and (optional) port of the proxy server, for example
1429 1436 "myproxy:8000".
1430 1437
1431 1438 ``no``
1432 1439 Optional. Comma-separated list of host names that should bypass
1433 1440 the proxy.
1434 1441
1435 1442 ``passwd``
1436 1443 Optional. Password to authenticate with at the proxy server.
1437 1444
1438 1445 ``user``
1439 1446 Optional. User name to authenticate with at the proxy server.
1440 1447
1441 1448 ``always``
1442 1449 Optional. Always use the proxy, even for localhost and any entries
1443 1450 in ``http_proxy.no``. (default: False)
1444 1451
1445 1452 ``http``
1446 1453 ----------
1447 1454
1448 1455 Used to configure access to Mercurial repositories via HTTP.
1449 1456
1450 1457 ``timeout``
1451 1458 If set, blocking operations will timeout after that many seconds.
1452 1459 (default: None)
1453 1460
1454 1461 ``merge``
1455 1462 ---------
1456 1463
1457 1464 This section specifies behavior during merges and updates.
1458 1465
1459 1466 ``checkignored``
1460 1467 Controls behavior when an ignored file on disk has the same name as a tracked
1461 1468 file in the changeset being merged or updated to, and has different
1462 1469 contents. Options are ``abort``, ``warn`` and ``ignore``. With ``abort``,
1463 1470 abort on such files. With ``warn``, warn on such files and back them up as
1464 1471 ``.orig``. With ``ignore``, don't print a warning and back them up as
1465 1472 ``.orig``. (default: ``abort``)
1466 1473
1467 1474 ``checkunknown``
1468 1475 Controls behavior when an unknown file that isn't ignored has the same name
1469 1476 as a tracked file in the changeset being merged or updated to, and has
1470 1477 different contents. Similar to ``merge.checkignored``, except for files that
1471 1478 are not ignored. (default: ``abort``)
1472 1479
1473 1480 ``on-failure``
1474 1481 When set to ``continue`` (the default), the merge process attempts to
1475 1482 merge all unresolved files using the merge chosen tool, regardless of
1476 1483 whether previous file merge attempts during the process succeeded or not.
1477 1484 Setting this to ``prompt`` will prompt after any merge failure continue
1478 1485 or halt the merge process. Setting this to ``halt`` will automatically
1479 1486 halt the merge process on any merge tool failure. The merge process
1480 1487 can be restarted by using the ``resolve`` command. When a merge is
1481 1488 halted, the repository is left in a normal ``unresolved`` merge state.
1482 1489 (default: ``continue``)
1483 1490
1484 1491 ``strict-capability-check``
1485 1492 Whether capabilities of internal merge tools are checked strictly
1486 1493 or not, while examining rules to decide merge tool to be used.
1487 1494 (default: False)
1488 1495
1489 1496 ``merge-patterns``
1490 1497 ------------------
1491 1498
1492 1499 This section specifies merge tools to associate with particular file
1493 1500 patterns. Tools matched here will take precedence over the default
1494 1501 merge tool. Patterns are globs by default, rooted at the repository
1495 1502 root.
1496 1503
1497 1504 Example::
1498 1505
1499 1506 [merge-patterns]
1500 1507 **.c = kdiff3
1501 1508 **.jpg = myimgmerge
1502 1509
1503 1510 ``merge-tools``
1504 1511 ---------------
1505 1512
1506 1513 This section configures external merge tools to use for file-level
1507 1514 merges. This section has likely been preconfigured at install time.
1508 1515 Use :hg:`config merge-tools` to check the existing configuration.
1509 1516 Also see :hg:`help merge-tools` for more details.
1510 1517
1511 1518 Example ``~/.hgrc``::
1512 1519
1513 1520 [merge-tools]
1514 1521 # Override stock tool location
1515 1522 kdiff3.executable = ~/bin/kdiff3
1516 1523 # Specify command line
1517 1524 kdiff3.args = $base $local $other -o $output
1518 1525 # Give higher priority
1519 1526 kdiff3.priority = 1
1520 1527
1521 1528 # Changing the priority of preconfigured tool
1522 1529 meld.priority = 0
1523 1530
1524 1531 # Disable a preconfigured tool
1525 1532 vimdiff.disabled = yes
1526 1533
1527 1534 # Define new tool
1528 1535 myHtmlTool.args = -m $local $other $base $output
1529 1536 myHtmlTool.regkey = Software\FooSoftware\HtmlMerge
1530 1537 myHtmlTool.priority = 1
1531 1538
1532 1539 Supported arguments:
1533 1540
1534 1541 ``priority``
1535 1542 The priority in which to evaluate this tool.
1536 1543 (default: 0)
1537 1544
1538 1545 ``executable``
1539 1546 Either just the name of the executable or its pathname.
1540 1547
1541 1548 .. container:: windows
1542 1549
1543 1550 On Windows, the path can use environment variables with ${ProgramFiles}
1544 1551 syntax.
1545 1552
1546 1553 (default: the tool name)
1547 1554
1548 1555 ``args``
1549 1556 The arguments to pass to the tool executable. You can refer to the
1550 1557 files being merged as well as the output file through these
1551 1558 variables: ``$base``, ``$local``, ``$other``, ``$output``.
1552 1559
1553 1560 The meaning of ``$local`` and ``$other`` can vary depending on which action is
1554 1561 being performed. During an update or merge, ``$local`` represents the original
1555 1562 state of the file, while ``$other`` represents the commit you are updating to or
1556 1563 the commit you are merging with. During a rebase, ``$local`` represents the
1557 1564 destination of the rebase, and ``$other`` represents the commit being rebased.
1558 1565
1559 1566 Some operations define custom labels to assist with identifying the revisions,
1560 1567 accessible via ``$labellocal``, ``$labelother``, and ``$labelbase``. If custom
1561 1568 labels are not available, these will be ``local``, ``other``, and ``base``,
1562 1569 respectively.
1563 1570 (default: ``$local $base $other``)
1564 1571
1565 1572 ``premerge``
1566 1573 Attempt to run internal non-interactive 3-way merge tool before
1567 1574 launching external tool. Options are ``true``, ``false``, ``keep``,
1568 1575 ``keep-merge3``, or ``keep-mergediff`` (experimental). The ``keep`` option
1569 1576 will leave markers in the file if the premerge fails. The ``keep-merge3``
1570 1577 will do the same but include information about the base of the merge in the
1571 1578 marker (see internal :merge3 in :hg:`help merge-tools`). The
1572 1579 ``keep-mergediff`` option is similar but uses a different marker style
1573 1580 (see internal :merge3 in :hg:`help merge-tools`). (default: True)
1574 1581
1575 1582 ``binary``
1576 1583 This tool can merge binary files. (default: False, unless tool
1577 1584 was selected by file pattern match)
1578 1585
1579 1586 ``symlink``
1580 1587 This tool can merge symlinks. (default: False)
1581 1588
1582 1589 ``check``
1583 1590 A list of merge success-checking options:
1584 1591
1585 1592 ``changed``
1586 1593 Ask whether merge was successful when the merged file shows no changes.
1587 1594 ``conflicts``
1588 1595 Check whether there are conflicts even though the tool reported success.
1589 1596 ``prompt``
1590 1597 Always prompt for merge success, regardless of success reported by tool.
1591 1598
1592 1599 ``fixeol``
1593 1600 Attempt to fix up EOL changes caused by the merge tool.
1594 1601 (default: False)
1595 1602
1596 1603 ``gui``
1597 1604 This tool requires a graphical interface to run. (default: False)
1598 1605
1599 1606 ``mergemarkers``
1600 1607 Controls whether the labels passed via ``$labellocal``, ``$labelother``, and
1601 1608 ``$labelbase`` are ``detailed`` (respecting ``mergemarkertemplate``) or
1602 1609 ``basic``. If ``premerge`` is ``keep`` or ``keep-merge3``, the conflict
1603 1610 markers generated during premerge will be ``detailed`` if either this option or
1604 1611 the corresponding option in the ``[ui]`` section is ``detailed``.
1605 1612 (default: ``basic``)
1606 1613
1607 1614 ``mergemarkertemplate``
1608 1615 This setting can be used to override ``mergemarker`` from the
1609 1616 ``[command-templates]`` section on a per-tool basis; this applies to the
1610 1617 ``$label``-prefixed variables and to the conflict markers that are generated
1611 1618 if ``premerge`` is ``keep` or ``keep-merge3``. See the corresponding variable
1612 1619 in ``[ui]`` for more information.
1613 1620
1614 1621 .. container:: windows
1615 1622
1616 1623 ``regkey``
1617 1624 Windows registry key which describes install location of this
1618 1625 tool. Mercurial will search for this key first under
1619 1626 ``HKEY_CURRENT_USER`` and then under ``HKEY_LOCAL_MACHINE``.
1620 1627 (default: None)
1621 1628
1622 1629 ``regkeyalt``
1623 1630 An alternate Windows registry key to try if the first key is not
1624 1631 found. The alternate key uses the same ``regname`` and ``regappend``
1625 1632 semantics of the primary key. The most common use for this key
1626 1633 is to search for 32bit applications on 64bit operating systems.
1627 1634 (default: None)
1628 1635
1629 1636 ``regname``
1630 1637 Name of value to read from specified registry key.
1631 1638 (default: the unnamed (default) value)
1632 1639
1633 1640 ``regappend``
1634 1641 String to append to the value read from the registry, typically
1635 1642 the executable name of the tool.
1636 1643 (default: None)
1637 1644
1638 1645 ``pager``
1639 1646 ---------
1640 1647
1641 1648 Setting used to control when to paginate and with what external tool. See
1642 1649 :hg:`help pager` for details.
1643 1650
1644 1651 ``pager``
1645 1652 Define the external tool used as pager.
1646 1653
1647 1654 If no pager is set, Mercurial uses the environment variable $PAGER.
1648 1655 If neither pager.pager, nor $PAGER is set, a default pager will be
1649 1656 used, typically `less` on Unix and `more` on Windows. Example::
1650 1657
1651 1658 [pager]
1652 1659 pager = less -FRX
1653 1660
1654 1661 ``ignore``
1655 1662 List of commands to disable the pager for. Example::
1656 1663
1657 1664 [pager]
1658 1665 ignore = version, help, update
1659 1666
1660 1667 ``patch``
1661 1668 ---------
1662 1669
1663 1670 Settings used when applying patches, for instance through the 'import'
1664 1671 command or with Mercurial Queues extension.
1665 1672
1666 1673 ``eol``
1667 1674 When set to 'strict' patch content and patched files end of lines
1668 1675 are preserved. When set to ``lf`` or ``crlf``, both files end of
1669 1676 lines are ignored when patching and the result line endings are
1670 1677 normalized to either LF (Unix) or CRLF (Windows). When set to
1671 1678 ``auto``, end of lines are again ignored while patching but line
1672 1679 endings in patched files are normalized to their original setting
1673 1680 on a per-file basis. If target file does not exist or has no end
1674 1681 of line, patch line endings are preserved.
1675 1682 (default: strict)
1676 1683
1677 1684 ``fuzz``
1678 1685 The number of lines of 'fuzz' to allow when applying patches. This
1679 1686 controls how much context the patcher is allowed to ignore when
1680 1687 trying to apply a patch.
1681 1688 (default: 2)
1682 1689
1683 1690 ``paths``
1684 1691 ---------
1685 1692
1686 1693 Assigns symbolic names and behavior to repositories.
1687 1694
1688 1695 Options are symbolic names defining the URL or directory that is the
1689 1696 location of the repository. Example::
1690 1697
1691 1698 [paths]
1692 1699 my_server = https://example.com/my_repo
1693 1700 local_path = /home/me/repo
1694 1701
1695 1702 These symbolic names can be used from the command line. To pull
1696 1703 from ``my_server``: :hg:`pull my_server`. To push to ``local_path``:
1697 1704 :hg:`push local_path`.
1698 1705
1699 1706 Options containing colons (``:``) denote sub-options that can influence
1700 1707 behavior for that specific path. Example::
1701 1708
1702 1709 [paths]
1703 1710 my_server = https://example.com/my_path
1704 1711 my_server:pushurl = ssh://example.com/my_path
1705 1712
1706 1713 The following sub-options can be defined:
1707 1714
1708 1715 ``pushurl``
1709 1716 The URL to use for push operations. If not defined, the location
1710 1717 defined by the path's main entry is used.
1711 1718
1712 1719 ``pushrev``
1713 1720 A revset defining which revisions to push by default.
1714 1721
1715 1722 When :hg:`push` is executed without a ``-r`` argument, the revset
1716 1723 defined by this sub-option is evaluated to determine what to push.
1717 1724
1718 1725 For example, a value of ``.`` will push the working directory's
1719 1726 revision by default.
1720 1727
1721 1728 Revsets specifying bookmarks will not result in the bookmark being
1722 1729 pushed.
1723 1730
1724 1731 The following special named paths exist:
1725 1732
1726 1733 ``default``
1727 1734 The URL or directory to use when no source or remote is specified.
1728 1735
1729 1736 :hg:`clone` will automatically define this path to the location the
1730 1737 repository was cloned from.
1731 1738
1732 1739 ``default-push``
1733 1740 (deprecated) The URL or directory for the default :hg:`push` location.
1734 1741 ``default:pushurl`` should be used instead.
1735 1742
1736 1743 ``phases``
1737 1744 ----------
1738 1745
1739 1746 Specifies default handling of phases. See :hg:`help phases` for more
1740 1747 information about working with phases.
1741 1748
1742 1749 ``publish``
1743 1750 Controls draft phase behavior when working as a server. When true,
1744 1751 pushed changesets are set to public in both client and server and
1745 1752 pulled or cloned changesets are set to public in the client.
1746 1753 (default: True)
1747 1754
1748 1755 ``new-commit``
1749 1756 Phase of newly-created commits.
1750 1757 (default: draft)
1751 1758
1752 1759 ``checksubrepos``
1753 1760 Check the phase of the current revision of each subrepository. Allowed
1754 1761 values are "ignore", "follow" and "abort". For settings other than
1755 1762 "ignore", the phase of the current revision of each subrepository is
1756 1763 checked before committing the parent repository. If any of those phases is
1757 1764 greater than the phase of the parent repository (e.g. if a subrepo is in a
1758 1765 "secret" phase while the parent repo is in "draft" phase), the commit is
1759 1766 either aborted (if checksubrepos is set to "abort") or the higher phase is
1760 1767 used for the parent repository commit (if set to "follow").
1761 1768 (default: follow)
1762 1769
1763 1770
1764 1771 ``profiling``
1765 1772 -------------
1766 1773
1767 1774 Specifies profiling type, format, and file output. Two profilers are
1768 1775 supported: an instrumenting profiler (named ``ls``), and a sampling
1769 1776 profiler (named ``stat``).
1770 1777
1771 1778 In this section description, 'profiling data' stands for the raw data
1772 1779 collected during profiling, while 'profiling report' stands for a
1773 1780 statistical text report generated from the profiling data.
1774 1781
1775 1782 ``enabled``
1776 1783 Enable the profiler.
1777 1784 (default: false)
1778 1785
1779 1786 This is equivalent to passing ``--profile`` on the command line.
1780 1787
1781 1788 ``type``
1782 1789 The type of profiler to use.
1783 1790 (default: stat)
1784 1791
1785 1792 ``ls``
1786 1793 Use Python's built-in instrumenting profiler. This profiler
1787 1794 works on all platforms, but each line number it reports is the
1788 1795 first line of a function. This restriction makes it difficult to
1789 1796 identify the expensive parts of a non-trivial function.
1790 1797 ``stat``
1791 1798 Use a statistical profiler, statprof. This profiler is most
1792 1799 useful for profiling commands that run for longer than about 0.1
1793 1800 seconds.
1794 1801
1795 1802 ``format``
1796 1803 Profiling format. Specific to the ``ls`` instrumenting profiler.
1797 1804 (default: text)
1798 1805
1799 1806 ``text``
1800 1807 Generate a profiling report. When saving to a file, it should be
1801 1808 noted that only the report is saved, and the profiling data is
1802 1809 not kept.
1803 1810 ``kcachegrind``
1804 1811 Format profiling data for kcachegrind use: when saving to a
1805 1812 file, the generated file can directly be loaded into
1806 1813 kcachegrind.
1807 1814
1808 1815 ``statformat``
1809 1816 Profiling format for the ``stat`` profiler.
1810 1817 (default: hotpath)
1811 1818
1812 1819 ``hotpath``
1813 1820 Show a tree-based display containing the hot path of execution (where
1814 1821 most time was spent).
1815 1822 ``bymethod``
1816 1823 Show a table of methods ordered by how frequently they are active.
1817 1824 ``byline``
1818 1825 Show a table of lines in files ordered by how frequently they are active.
1819 1826 ``json``
1820 1827 Render profiling data as JSON.
1821 1828
1822 1829 ``frequency``
1823 1830 Sampling frequency. Specific to the ``stat`` sampling profiler.
1824 1831 (default: 1000)
1825 1832
1826 1833 ``output``
1827 1834 File path where profiling data or report should be saved. If the
1828 1835 file exists, it is replaced. (default: None, data is printed on
1829 1836 stderr)
1830 1837
1831 1838 ``sort``
1832 1839 Sort field. Specific to the ``ls`` instrumenting profiler.
1833 1840 One of ``callcount``, ``reccallcount``, ``totaltime`` and
1834 1841 ``inlinetime``.
1835 1842 (default: inlinetime)
1836 1843
1837 1844 ``time-track``
1838 1845 Control if the stat profiler track ``cpu`` or ``real`` time.
1839 1846 (default: ``cpu`` on Windows, otherwise ``real``)
1840 1847
1841 1848 ``limit``
1842 1849 Number of lines to show. Specific to the ``ls`` instrumenting profiler.
1843 1850 (default: 30)
1844 1851
1845 1852 ``nested``
1846 1853 Show at most this number of lines of drill-down info after each main entry.
1847 1854 This can help explain the difference between Total and Inline.
1848 1855 Specific to the ``ls`` instrumenting profiler.
1849 1856 (default: 0)
1850 1857
1851 1858 ``showmin``
1852 1859 Minimum fraction of samples an entry must have for it to be displayed.
1853 1860 Can be specified as a float between ``0.0`` and ``1.0`` or can have a
1854 1861 ``%`` afterwards to allow values up to ``100``. e.g. ``5%``.
1855 1862
1856 1863 Only used by the ``stat`` profiler.
1857 1864
1858 1865 For the ``hotpath`` format, default is ``0.05``.
1859 1866 For the ``chrome`` format, default is ``0.005``.
1860 1867
1861 1868 The option is unused on other formats.
1862 1869
1863 1870 ``showmax``
1864 1871 Maximum fraction of samples an entry can have before it is ignored in
1865 1872 display. Values format is the same as ``showmin``.
1866 1873
1867 1874 Only used by the ``stat`` profiler.
1868 1875
1869 1876 For the ``chrome`` format, default is ``0.999``.
1870 1877
1871 1878 The option is unused on other formats.
1872 1879
1873 1880 ``showtime``
1874 1881 Show time taken as absolute durations, in addition to percentages.
1875 1882 Only used by the ``hotpath`` format.
1876 1883 (default: true)
1877 1884
1878 1885 ``progress``
1879 1886 ------------
1880 1887
1881 1888 Mercurial commands can draw progress bars that are as informative as
1882 1889 possible. Some progress bars only offer indeterminate information, while others
1883 1890 have a definite end point.
1884 1891
1885 1892 ``debug``
1886 1893 Whether to print debug info when updating the progress bar. (default: False)
1887 1894
1888 1895 ``delay``
1889 1896 Number of seconds (float) before showing the progress bar. (default: 3)
1890 1897
1891 1898 ``changedelay``
1892 1899 Minimum delay before showing a new topic. When set to less than 3 * refresh,
1893 1900 that value will be used instead. (default: 1)
1894 1901
1895 1902 ``estimateinterval``
1896 1903 Maximum sampling interval in seconds for speed and estimated time
1897 1904 calculation. (default: 60)
1898 1905
1899 1906 ``refresh``
1900 1907 Time in seconds between refreshes of the progress bar. (default: 0.1)
1901 1908
1902 1909 ``format``
1903 1910 Format of the progress bar.
1904 1911
1905 1912 Valid entries for the format field are ``topic``, ``bar``, ``number``,
1906 1913 ``unit``, ``estimate``, ``speed``, and ``item``. ``item`` defaults to the
1907 1914 last 20 characters of the item, but this can be changed by adding either
1908 1915 ``-<num>`` which would take the last num characters, or ``+<num>`` for the
1909 1916 first num characters.
1910 1917
1911 1918 (default: topic bar number estimate)
1912 1919
1913 1920 ``width``
1914 1921 If set, the maximum width of the progress information (that is, min(width,
1915 1922 term width) will be used).
1916 1923
1917 1924 ``clear-complete``
1918 1925 Clear the progress bar after it's done. (default: True)
1919 1926
1920 1927 ``disable``
1921 1928 If true, don't show a progress bar.
1922 1929
1923 1930 ``assume-tty``
1924 1931 If true, ALWAYS show a progress bar, unless disable is given.
1925 1932
1926 1933 ``rebase``
1927 1934 ----------
1928 1935
1929 1936 ``evolution.allowdivergence``
1930 1937 Default to False, when True allow creating divergence when performing
1931 1938 rebase of obsolete changesets.
1932 1939
1933 1940 ``revsetalias``
1934 1941 ---------------
1935 1942
1936 1943 Alias definitions for revsets. See :hg:`help revsets` for details.
1937 1944
1938 1945 ``rewrite``
1939 1946 -----------
1940 1947
1941 1948 ``backup-bundle``
1942 1949 Whether to save stripped changesets to a bundle file. (default: True)
1943 1950
1944 1951 ``update-timestamp``
1945 1952 If true, updates the date and time of the changeset to current. It is only
1946 1953 applicable for `hg amend`, `hg commit --amend` and `hg uncommit` in the
1947 1954 current version.
1948 1955
1949 1956 ``empty-successor``
1950 1957
1951 1958 Control what happens with empty successors that are the result of rewrite
1952 1959 operations. If set to ``skip``, the successor is not created. If set to
1953 1960 ``keep``, the empty successor is created and kept.
1954 1961
1955 1962 Currently, only the rebase and absorb commands consider this configuration.
1956 1963 (EXPERIMENTAL)
1957 1964
1958 1965 ``share``
1959 1966 ---------
1960 1967
1961 1968 ``safe-mismatch.source-safe``
1962 1969
1963 1970 Controls what happens when the shared repository does not use the
1964 1971 share-safe mechanism but its source repository does.
1965 1972
1966 1973 Possible values are `abort` (default), `allow`, `upgrade-abort` and
1967 1974 `upgrade-abort`.
1968 1975
1969 1976 ``abort``
1970 1977 Disallows running any command and aborts
1971 1978 ``allow``
1972 1979 Respects the feature presence in the share source
1973 1980 ``upgrade-abort``
1974 1981 tries to upgrade the share to use share-safe; if it fails, aborts
1975 1982 ``upgrade-allow``
1976 1983 tries to upgrade the share; if it fails, continue by
1977 1984 respecting the share source setting
1978 1985
1979 1986 Check :hg:`help config format.use-share-safe` for details about the
1980 1987 share-safe feature.
1981 1988
1982 1989 ``safe-mismatch.source-safe.warn``
1983 1990 Shows a warning on operations if the shared repository does not use
1984 1991 share-safe, but the source repository does.
1985 1992 (default: True)
1986 1993
1987 1994 ``safe-mismatch.source-not-safe``
1988 1995
1989 1996 Controls what happens when the shared repository uses the share-safe
1990 1997 mechanism but its source does not.
1991 1998
1992 1999 Possible values are `abort` (default), `allow`, `downgrade-abort` and
1993 2000 `downgrade-abort`.
1994 2001
1995 2002 ``abort``
1996 2003 Disallows running any command and aborts
1997 2004 ``allow``
1998 2005 Respects the feature presence in the share source
1999 2006 ``downgrade-abort``
2000 2007 tries to downgrade the share to not use share-safe; if it fails, aborts
2001 2008 ``downgrade-allow``
2002 2009 tries to downgrade the share to not use share-safe;
2003 2010 if it fails, continue by respecting the shared source setting
2004 2011
2005 2012 Check :hg:`help config format.use-share-safe` for details about the
2006 2013 share-safe feature.
2007 2014
2008 2015 ``safe-mismatch.source-not-safe.warn``
2009 2016 Shows a warning on operations if the shared repository uses share-safe,
2010 2017 but the source repository does not.
2011 2018 (default: True)
2012 2019
2013 2020 ``storage``
2014 2021 -----------
2015 2022
2016 2023 Control the strategy Mercurial uses internally to store history. Options in this
2017 2024 category impact performance and repository size.
2018 2025
2019 2026 ``revlog.optimize-delta-parent-choice``
2020 2027 When storing a merge revision, both parents will be equally considered as
2021 2028 a possible delta base. This results in better delta selection and improved
2022 2029 revlog compression. This option is enabled by default.
2023 2030
2024 2031 Turning this option off can result in large increase of repository size for
2025 2032 repository with many merges.
2026 2033
2027 2034 ``revlog.persistent-nodemap.mmap``
2028 2035 Whether to use the Operating System "memory mapping" feature (when
2029 2036 possible) to access the persistent nodemap data. This improve performance
2030 2037 and reduce memory pressure.
2031 2038
2032 2039 Default to True.
2033 2040
2034 2041 For details on the "persistent-nodemap" feature, see:
2035 2042 :hg:`help config format.use-persistent-nodemap`.
2036 2043
2037 2044 ``revlog.persistent-nodemap.slow-path``
2038 2045 Control the behavior of Merucrial when using a repository with "persistent"
2039 2046 nodemap with an installation of Mercurial without a fast implementation for
2040 2047 the feature:
2041 2048
2042 2049 ``allow``: Silently use the slower implementation to access the repository.
2043 2050 ``warn``: Warn, but use the slower implementation to access the repository.
2044 2051 ``abort``: Prevent access to such repositories. (This is the default)
2045 2052
2046 2053 For details on the "persistent-nodemap" feature, see:
2047 2054 :hg:`help config format.use-persistent-nodemap`.
2048 2055
2049 2056 ``revlog.reuse-external-delta-parent``
2050 2057 Control the order in which delta parents are considered when adding new
2051 2058 revisions from an external source.
2052 2059 (typically: apply bundle from `hg pull` or `hg push`).
2053 2060
2054 2061 New revisions are usually provided as a delta against other revisions. By
2055 2062 default, Mercurial will try to reuse this delta first, therefore using the
2056 2063 same "delta parent" as the source. Directly using delta's from the source
2057 2064 reduces CPU usage and usually speeds up operation. However, in some case,
2058 2065 the source might have sub-optimal delta bases and forcing their reevaluation
2059 2066 is useful. For example, pushes from an old client could have sub-optimal
2060 2067 delta's parent that the server want to optimize. (lack of general delta, bad
2061 2068 parents, choice, lack of sparse-revlog, etc).
2062 2069
2063 2070 This option is enabled by default. Turning it off will ensure bad delta
2064 2071 parent choices from older client do not propagate to this repository, at
2065 2072 the cost of a small increase in CPU consumption.
2066 2073
2067 2074 Note: this option only control the order in which delta parents are
2068 2075 considered. Even when disabled, the existing delta from the source will be
2069 2076 reused if the same delta parent is selected.
2070 2077
2071 2078 ``revlog.reuse-external-delta``
2072 2079 Control the reuse of delta from external source.
2073 2080 (typically: apply bundle from `hg pull` or `hg push`).
2074 2081
2075 2082 New revisions are usually provided as a delta against another revision. By
2076 2083 default, Mercurial will not recompute the same delta again, trusting
2077 2084 externally provided deltas. There have been rare cases of small adjustment
2078 2085 to the diffing algorithm in the past. So in some rare case, recomputing
2079 2086 delta provided by ancient clients can provides better results. Disabling
2080 2087 this option means going through a full delta recomputation for all incoming
2081 2088 revisions. It means a large increase in CPU usage and will slow operations
2082 2089 down.
2083 2090
2084 2091 This option is enabled by default. When disabled, it also disables the
2085 2092 related ``storage.revlog.reuse-external-delta-parent`` option.
2086 2093
2087 2094 ``revlog.zlib.level``
2088 2095 Zlib compression level used when storing data into the repository. Accepted
2089 2096 Value range from 1 (lowest compression) to 9 (highest compression). Zlib
2090 2097 default value is 6.
2091 2098
2092 2099
2093 2100 ``revlog.zstd.level``
2094 2101 zstd compression level used when storing data into the repository. Accepted
2095 2102 Value range from 1 (lowest compression) to 22 (highest compression).
2096 2103 (default 3)
2097 2104
2098 2105 ``server``
2099 2106 ----------
2100 2107
2101 2108 Controls generic server settings.
2102 2109
2103 2110 ``bookmarks-pushkey-compat``
2104 2111 Trigger pushkey hook when being pushed bookmark updates. This config exist
2105 2112 for compatibility purpose (default to True)
2106 2113
2107 2114 If you use ``pushkey`` and ``pre-pushkey`` hooks to control bookmark
2108 2115 movement we recommend you migrate them to ``txnclose-bookmark`` and
2109 2116 ``pretxnclose-bookmark``.
2110 2117
2111 2118 ``compressionengines``
2112 2119 List of compression engines and their relative priority to advertise
2113 2120 to clients.
2114 2121
2115 2122 The order of compression engines determines their priority, the first
2116 2123 having the highest priority. If a compression engine is not listed
2117 2124 here, it won't be advertised to clients.
2118 2125
2119 2126 If not set (the default), built-in defaults are used. Run
2120 2127 :hg:`debuginstall` to list available compression engines and their
2121 2128 default wire protocol priority.
2122 2129
2123 2130 Older Mercurial clients only support zlib compression and this setting
2124 2131 has no effect for legacy clients.
2125 2132
2126 2133 ``uncompressed``
2127 2134 Whether to allow clients to clone a repository using the
2128 2135 uncompressed streaming protocol. This transfers about 40% more
2129 2136 data than a regular clone, but uses less memory and CPU on both
2130 2137 server and client. Over a LAN (100 Mbps or better) or a very fast
2131 2138 WAN, an uncompressed streaming clone is a lot faster (~10x) than a
2132 2139 regular clone. Over most WAN connections (anything slower than
2133 2140 about 6 Mbps), uncompressed streaming is slower, because of the
2134 2141 extra data transfer overhead. This mode will also temporarily hold
2135 2142 the write lock while determining what data to transfer.
2136 2143 (default: True)
2137 2144
2138 2145 ``uncompressedallowsecret``
2139 2146 Whether to allow stream clones when the repository contains secret
2140 2147 changesets. (default: False)
2141 2148
2142 2149 ``preferuncompressed``
2143 2150 When set, clients will try to use the uncompressed streaming
2144 2151 protocol. (default: False)
2145 2152
2146 2153 ``disablefullbundle``
2147 2154 When set, servers will refuse attempts to do pull-based clones.
2148 2155 If this option is set, ``preferuncompressed`` and/or clone bundles
2149 2156 are highly recommended. Partial clones will still be allowed.
2150 2157 (default: False)
2151 2158
2152 2159 ``streamunbundle``
2153 2160 When set, servers will apply data sent from the client directly,
2154 2161 otherwise it will be written to a temporary file first. This option
2155 2162 effectively prevents concurrent pushes.
2156 2163
2157 2164 ``pullbundle``
2158 2165 When set, the server will check pullbundle.manifest for bundles
2159 2166 covering the requested heads and common nodes. The first matching
2160 2167 entry will be streamed to the client.
2161 2168
2162 2169 For HTTP transport, the stream will still use zlib compression
2163 2170 for older clients.
2164 2171
2165 2172 ``concurrent-push-mode``
2166 2173 Level of allowed race condition between two pushing clients.
2167 2174
2168 2175 - 'strict': push is abort if another client touched the repository
2169 2176 while the push was preparing.
2170 2177 - 'check-related': push is only aborted if it affects head that got also
2171 2178 affected while the push was preparing. (default since 5.4)
2172 2179
2173 2180 'check-related' only takes effect for compatible clients (version
2174 2181 4.3 and later). Older clients will use 'strict'.
2175 2182
2176 2183 ``validate``
2177 2184 Whether to validate the completeness of pushed changesets by
2178 2185 checking that all new file revisions specified in manifests are
2179 2186 present. (default: False)
2180 2187
2181 2188 ``maxhttpheaderlen``
2182 2189 Instruct HTTP clients not to send request headers longer than this
2183 2190 many bytes. (default: 1024)
2184 2191
2185 2192 ``bundle1``
2186 2193 Whether to allow clients to push and pull using the legacy bundle1
2187 2194 exchange format. (default: True)
2188 2195
2189 2196 ``bundle1gd``
2190 2197 Like ``bundle1`` but only used if the repository is using the
2191 2198 *generaldelta* storage format. (default: True)
2192 2199
2193 2200 ``bundle1.push``
2194 2201 Whether to allow clients to push using the legacy bundle1 exchange
2195 2202 format. (default: True)
2196 2203
2197 2204 ``bundle1gd.push``
2198 2205 Like ``bundle1.push`` but only used if the repository is using the
2199 2206 *generaldelta* storage format. (default: True)
2200 2207
2201 2208 ``bundle1.pull``
2202 2209 Whether to allow clients to pull using the legacy bundle1 exchange
2203 2210 format. (default: True)
2204 2211
2205 2212 ``bundle1gd.pull``
2206 2213 Like ``bundle1.pull`` but only used if the repository is using the
2207 2214 *generaldelta* storage format. (default: True)
2208 2215
2209 2216 Large repositories using the *generaldelta* storage format should
2210 2217 consider setting this option because converting *generaldelta*
2211 2218 repositories to the exchange format required by the bundle1 data
2212 2219 format can consume a lot of CPU.
2213 2220
2214 2221 ``bundle2.stream``
2215 2222 Whether to allow clients to pull using the bundle2 streaming protocol.
2216 2223 (default: True)
2217 2224
2218 2225 ``zliblevel``
2219 2226 Integer between ``-1`` and ``9`` that controls the zlib compression level
2220 2227 for wire protocol commands that send zlib compressed output (notably the
2221 2228 commands that send repository history data).
2222 2229
2223 2230 The default (``-1``) uses the default zlib compression level, which is
2224 2231 likely equivalent to ``6``. ``0`` means no compression. ``9`` means
2225 2232 maximum compression.
2226 2233
2227 2234 Setting this option allows server operators to make trade-offs between
2228 2235 bandwidth and CPU used. Lowering the compression lowers CPU utilization
2229 2236 but sends more bytes to clients.
2230 2237
2231 2238 This option only impacts the HTTP server.
2232 2239
2233 2240 ``zstdlevel``
2234 2241 Integer between ``1`` and ``22`` that controls the zstd compression level
2235 2242 for wire protocol commands. ``1`` is the minimal amount of compression and
2236 2243 ``22`` is the highest amount of compression.
2237 2244
2238 2245 The default (``3``) should be significantly faster than zlib while likely
2239 2246 delivering better compression ratios.
2240 2247
2241 2248 This option only impacts the HTTP server.
2242 2249
2243 2250 See also ``server.zliblevel``.
2244 2251
2245 2252 ``view``
2246 2253 Repository filter used when exchanging revisions with the peer.
2247 2254
2248 2255 The default view (``served``) excludes secret and hidden changesets.
2249 2256 Another useful value is ``immutable`` (no draft, secret or hidden
2250 2257 changesets). (EXPERIMENTAL)
2251 2258
2252 2259 ``smtp``
2253 2260 --------
2254 2261
2255 2262 Configuration for extensions that need to send email messages.
2256 2263
2257 2264 ``host``
2258 2265 Host name of mail server, e.g. "mail.example.com".
2259 2266
2260 2267 ``port``
2261 2268 Optional. Port to connect to on mail server. (default: 465 if
2262 2269 ``tls`` is smtps; 25 otherwise)
2263 2270
2264 2271 ``tls``
2265 2272 Optional. Method to enable TLS when connecting to mail server: starttls,
2266 2273 smtps or none. (default: none)
2267 2274
2268 2275 ``username``
2269 2276 Optional. User name for authenticating with the SMTP server.
2270 2277 (default: None)
2271 2278
2272 2279 ``password``
2273 2280 Optional. Password for authenticating with the SMTP server. If not
2274 2281 specified, interactive sessions will prompt the user for a
2275 2282 password; non-interactive sessions will fail. (default: None)
2276 2283
2277 2284 ``local_hostname``
2278 2285 Optional. The hostname that the sender can use to identify
2279 2286 itself to the MTA.
2280 2287
2281 2288
2282 2289 ``subpaths``
2283 2290 ------------
2284 2291
2285 2292 Subrepository source URLs can go stale if a remote server changes name
2286 2293 or becomes temporarily unavailable. This section lets you define
2287 2294 rewrite rules of the form::
2288 2295
2289 2296 <pattern> = <replacement>
2290 2297
2291 2298 where ``pattern`` is a regular expression matching a subrepository
2292 2299 source URL and ``replacement`` is the replacement string used to
2293 2300 rewrite it. Groups can be matched in ``pattern`` and referenced in
2294 2301 ``replacements``. For instance::
2295 2302
2296 2303 http://server/(.*)-hg/ = http://hg.server/\1/
2297 2304
2298 2305 rewrites ``http://server/foo-hg/`` into ``http://hg.server/foo/``.
2299 2306
2300 2307 Relative subrepository paths are first made absolute, and the
2301 2308 rewrite rules are then applied on the full (absolute) path. If ``pattern``
2302 2309 doesn't match the full path, an attempt is made to apply it on the
2303 2310 relative path alone. The rules are applied in definition order.
2304 2311
2305 2312 ``subrepos``
2306 2313 ------------
2307 2314
2308 2315 This section contains options that control the behavior of the
2309 2316 subrepositories feature. See also :hg:`help subrepos`.
2310 2317
2311 2318 Security note: auditing in Mercurial is known to be insufficient to
2312 2319 prevent clone-time code execution with carefully constructed Git
2313 2320 subrepos. It is unknown if a similar detect is present in Subversion
2314 2321 subrepos. Both Git and Subversion subrepos are disabled by default
2315 2322 out of security concerns. These subrepo types can be enabled using
2316 2323 the respective options below.
2317 2324
2318 2325 ``allowed``
2319 2326 Whether subrepositories are allowed in the working directory.
2320 2327
2321 2328 When false, commands involving subrepositories (like :hg:`update`)
2322 2329 will fail for all subrepository types.
2323 2330 (default: true)
2324 2331
2325 2332 ``hg:allowed``
2326 2333 Whether Mercurial subrepositories are allowed in the working
2327 2334 directory. This option only has an effect if ``subrepos.allowed``
2328 2335 is true.
2329 2336 (default: true)
2330 2337
2331 2338 ``git:allowed``
2332 2339 Whether Git subrepositories are allowed in the working directory.
2333 2340 This option only has an effect if ``subrepos.allowed`` is true.
2334 2341
2335 2342 See the security note above before enabling Git subrepos.
2336 2343 (default: false)
2337 2344
2338 2345 ``svn:allowed``
2339 2346 Whether Subversion subrepositories are allowed in the working
2340 2347 directory. This option only has an effect if ``subrepos.allowed``
2341 2348 is true.
2342 2349
2343 2350 See the security note above before enabling Subversion subrepos.
2344 2351 (default: false)
2345 2352
2346 2353 ``templatealias``
2347 2354 -----------------
2348 2355
2349 2356 Alias definitions for templates. See :hg:`help templates` for details.
2350 2357
2351 2358 ``templates``
2352 2359 -------------
2353 2360
2354 2361 Use the ``[templates]`` section to define template strings.
2355 2362 See :hg:`help templates` for details.
2356 2363
2357 2364 ``trusted``
2358 2365 -----------
2359 2366
2360 2367 Mercurial will not use the settings in the
2361 2368 ``.hg/hgrc`` file from a repository if it doesn't belong to a trusted
2362 2369 user or to a trusted group, as various hgrc features allow arbitrary
2363 2370 commands to be run. This issue is often encountered when configuring
2364 2371 hooks or extensions for shared repositories or servers. However,
2365 2372 the web interface will use some safe settings from the ``[web]``
2366 2373 section.
2367 2374
2368 2375 This section specifies what users and groups are trusted. The
2369 2376 current user is always trusted. To trust everybody, list a user or a
2370 2377 group with name ``*``. These settings must be placed in an
2371 2378 *already-trusted file* to take effect, such as ``$HOME/.hgrc`` of the
2372 2379 user or service running Mercurial.
2373 2380
2374 2381 ``users``
2375 2382 Comma-separated list of trusted users.
2376 2383
2377 2384 ``groups``
2378 2385 Comma-separated list of trusted groups.
2379 2386
2380 2387
2381 2388 ``ui``
2382 2389 ------
2383 2390
2384 2391 User interface controls.
2385 2392
2386 2393 ``archivemeta``
2387 2394 Whether to include the .hg_archival.txt file containing meta data
2388 2395 (hashes for the repository base and for tip) in archives created
2389 2396 by the :hg:`archive` command or downloaded via hgweb.
2390 2397 (default: True)
2391 2398
2392 2399 ``askusername``
2393 2400 Whether to prompt for a username when committing. If True, and
2394 2401 neither ``$HGUSER`` nor ``$EMAIL`` has been specified, then the user will
2395 2402 be prompted to enter a username. If no username is entered, the
2396 2403 default ``USER@HOST`` is used instead.
2397 2404 (default: False)
2398 2405
2399 2406 ``clonebundles``
2400 2407 Whether the "clone bundles" feature is enabled.
2401 2408
2402 2409 When enabled, :hg:`clone` may download and apply a server-advertised
2403 2410 bundle file from a URL instead of using the normal exchange mechanism.
2404 2411
2405 2412 This can likely result in faster and more reliable clones.
2406 2413
2407 2414 (default: True)
2408 2415
2409 2416 ``clonebundlefallback``
2410 2417 Whether failure to apply an advertised "clone bundle" from a server
2411 2418 should result in fallback to a regular clone.
2412 2419
2413 2420 This is disabled by default because servers advertising "clone
2414 2421 bundles" often do so to reduce server load. If advertised bundles
2415 2422 start mass failing and clients automatically fall back to a regular
2416 2423 clone, this would add significant and unexpected load to the server
2417 2424 since the server is expecting clone operations to be offloaded to
2418 2425 pre-generated bundles. Failing fast (the default behavior) ensures
2419 2426 clients don't overwhelm the server when "clone bundle" application
2420 2427 fails.
2421 2428
2422 2429 (default: False)
2423 2430
2424 2431 ``clonebundleprefers``
2425 2432 Defines preferences for which "clone bundles" to use.
2426 2433
2427 2434 Servers advertising "clone bundles" may advertise multiple available
2428 2435 bundles. Each bundle may have different attributes, such as the bundle
2429 2436 type and compression format. This option is used to prefer a particular
2430 2437 bundle over another.
2431 2438
2432 2439 The following keys are defined by Mercurial:
2433 2440
2434 2441 BUNDLESPEC
2435 2442 A bundle type specifier. These are strings passed to :hg:`bundle -t`.
2436 2443 e.g. ``gzip-v2`` or ``bzip2-v1``.
2437 2444
2438 2445 COMPRESSION
2439 2446 The compression format of the bundle. e.g. ``gzip`` and ``bzip2``.
2440 2447
2441 2448 Server operators may define custom keys.
2442 2449
2443 2450 Example values: ``COMPRESSION=bzip2``,
2444 2451 ``BUNDLESPEC=gzip-v2, COMPRESSION=gzip``.
2445 2452
2446 2453 By default, the first bundle advertised by the server is used.
2447 2454
2448 2455 ``color``
2449 2456 When to colorize output. Possible value are Boolean ("yes" or "no"), or
2450 2457 "debug", or "always". (default: "yes"). "yes" will use color whenever it
2451 2458 seems possible. See :hg:`help color` for details.
2452 2459
2453 2460 ``commitsubrepos``
2454 2461 Whether to commit modified subrepositories when committing the
2455 2462 parent repository. If False and one subrepository has uncommitted
2456 2463 changes, abort the commit.
2457 2464 (default: False)
2458 2465
2459 2466 ``debug``
2460 2467 Print debugging information. (default: False)
2461 2468
2462 2469 ``editor``
2463 2470 The editor to use during a commit. (default: ``$EDITOR`` or ``vi``)
2464 2471
2465 2472 ``fallbackencoding``
2466 2473 Encoding to try if it's not possible to decode the changelog using
2467 2474 UTF-8. (default: ISO-8859-1)
2468 2475
2469 2476 ``graphnodetemplate``
2470 2477 (DEPRECATED) Use ``command-templates.graphnode`` instead.
2471 2478
2472 2479 ``ignore``
2473 2480 A file to read per-user ignore patterns from. This file should be
2474 2481 in the same format as a repository-wide .hgignore file. Filenames
2475 2482 are relative to the repository root. This option supports hook syntax,
2476 2483 so if you want to specify multiple ignore files, you can do so by
2477 2484 setting something like ``ignore.other = ~/.hgignore2``. For details
2478 2485 of the ignore file format, see the ``hgignore(5)`` man page.
2479 2486
2480 2487 ``interactive``
2481 2488 Allow to prompt the user. (default: True)
2482 2489
2483 2490 ``interface``
2484 2491 Select the default interface for interactive features (default: text).
2485 2492 Possible values are 'text' and 'curses'.
2486 2493
2487 2494 ``interface.chunkselector``
2488 2495 Select the interface for change recording (e.g. :hg:`commit -i`).
2489 2496 Possible values are 'text' and 'curses'.
2490 2497 This config overrides the interface specified by ui.interface.
2491 2498
2492 2499 ``large-file-limit``
2493 2500 Largest file size that gives no memory use warning.
2494 2501 Possible values are integers or 0 to disable the check.
2495 2502 (default: 10000000)
2496 2503
2497 2504 ``logtemplate``
2498 2505 (DEPRECATED) Use ``command-templates.log`` instead.
2499 2506
2500 2507 ``merge``
2501 2508 The conflict resolution program to use during a manual merge.
2502 2509 For more information on merge tools see :hg:`help merge-tools`.
2503 2510 For configuring merge tools see the ``[merge-tools]`` section.
2504 2511
2505 2512 ``mergemarkers``
2506 2513 Sets the merge conflict marker label styling. The ``detailed`` style
2507 2514 uses the ``command-templates.mergemarker`` setting to style the labels.
2508 2515 The ``basic`` style just uses 'local' and 'other' as the marker label.
2509 2516 One of ``basic`` or ``detailed``.
2510 2517 (default: ``basic``)
2511 2518
2512 2519 ``mergemarkertemplate``
2513 2520 (DEPRECATED) Use ``command-templates.mergemarker`` instead.
2514 2521
2515 2522 ``message-output``
2516 2523 Where to write status and error messages. (default: ``stdio``)
2517 2524
2518 2525 ``channel``
2519 2526 Use separate channel for structured output. (Command-server only)
2520 2527 ``stderr``
2521 2528 Everything to stderr.
2522 2529 ``stdio``
2523 2530 Status to stdout, and error to stderr.
2524 2531
2525 2532 ``origbackuppath``
2526 2533 The path to a directory used to store generated .orig files. If the path is
2527 2534 not a directory, one will be created. If set, files stored in this
2528 2535 directory have the same name as the original file and do not have a .orig
2529 2536 suffix.
2530 2537
2531 2538 ``paginate``
2532 2539 Control the pagination of command output (default: True). See :hg:`help pager`
2533 2540 for details.
2534 2541
2535 2542 ``patch``
2536 2543 An optional external tool that ``hg import`` and some extensions
2537 2544 will use for applying patches. By default Mercurial uses an
2538 2545 internal patch utility. The external tool must work as the common
2539 2546 Unix ``patch`` program. In particular, it must accept a ``-p``
2540 2547 argument to strip patch headers, a ``-d`` argument to specify the
2541 2548 current directory, a file name to patch, and a patch file to take
2542 2549 from stdin.
2543 2550
2544 2551 It is possible to specify a patch tool together with extra
2545 2552 arguments. For example, setting this option to ``patch --merge``
2546 2553 will use the ``patch`` program with its 2-way merge option.
2547 2554
2548 2555 ``portablefilenames``
2549 2556 Check for portable filenames. Can be ``warn``, ``ignore`` or ``abort``.
2550 2557 (default: ``warn``)
2551 2558
2552 2559 ``warn``
2553 2560 Print a warning message on POSIX platforms, if a file with a non-portable
2554 2561 filename is added (e.g. a file with a name that can't be created on
2555 2562 Windows because it contains reserved parts like ``AUX``, reserved
2556 2563 characters like ``:``, or would cause a case collision with an existing
2557 2564 file).
2558 2565
2559 2566 ``ignore``
2560 2567 Don't print a warning.
2561 2568
2562 2569 ``abort``
2563 2570 The command is aborted.
2564 2571
2565 2572 ``true``
2566 2573 Alias for ``warn``.
2567 2574
2568 2575 ``false``
2569 2576 Alias for ``ignore``.
2570 2577
2571 2578 .. container:: windows
2572 2579
2573 2580 On Windows, this configuration option is ignored and the command aborted.
2574 2581
2575 2582 ``pre-merge-tool-output-template``
2576 2583 (DEPRECATED) Use ``command-template.pre-merge-tool-output`` instead.
2577 2584
2578 2585 ``quiet``
2579 2586 Reduce the amount of output printed.
2580 2587 (default: False)
2581 2588
2582 2589 ``relative-paths``
2583 2590 Prefer relative paths in the UI.
2584 2591
2585 2592 ``remotecmd``
2586 2593 Remote command to use for clone/push/pull operations.
2587 2594 (default: ``hg``)
2588 2595
2589 2596 ``report_untrusted``
2590 2597 Warn if a ``.hg/hgrc`` file is ignored due to not being owned by a
2591 2598 trusted user or group.
2592 2599 (default: True)
2593 2600
2594 2601 ``slash``
2595 2602 (Deprecated. Use ``slashpath`` template filter instead.)
2596 2603
2597 2604 Display paths using a slash (``/``) as the path separator. This
2598 2605 only makes a difference on systems where the default path
2599 2606 separator is not the slash character (e.g. Windows uses the
2600 2607 backslash character (``\``)).
2601 2608 (default: False)
2602 2609
2603 2610 ``statuscopies``
2604 2611 Display copies in the status command.
2605 2612
2606 2613 ``ssh``
2607 2614 Command to use for SSH connections. (default: ``ssh``)
2608 2615
2609 2616 ``ssherrorhint``
2610 2617 A hint shown to the user in the case of SSH error (e.g.
2611 2618 ``Please see http://company/internalwiki/ssh.html``)
2612 2619
2613 2620 ``strict``
2614 2621 Require exact command names, instead of allowing unambiguous
2615 2622 abbreviations. (default: False)
2616 2623
2617 2624 ``style``
2618 2625 Name of style to use for command output.
2619 2626
2620 2627 ``supportcontact``
2621 2628 A URL where users should report a Mercurial traceback. Use this if you are a
2622 2629 large organisation with its own Mercurial deployment process and crash
2623 2630 reports should be addressed to your internal support.
2624 2631
2625 2632 ``textwidth``
2626 2633 Maximum width of help text. A longer line generated by ``hg help`` or
2627 2634 ``hg subcommand --help`` will be broken after white space to get this
2628 2635 width or the terminal width, whichever comes first.
2629 2636 A non-positive value will disable this and the terminal width will be
2630 2637 used. (default: 78)
2631 2638
2632 2639 ``timeout``
2633 2640 The timeout used when a lock is held (in seconds), a negative value
2634 2641 means no timeout. (default: 600)
2635 2642
2636 2643 ``timeout.warn``
2637 2644 Time (in seconds) before a warning is printed about held lock. A negative
2638 2645 value means no warning. (default: 0)
2639 2646
2640 2647 ``traceback``
2641 2648 Mercurial always prints a traceback when an unknown exception
2642 2649 occurs. Setting this to True will make Mercurial print a traceback
2643 2650 on all exceptions, even those recognized by Mercurial (such as
2644 2651 IOError or MemoryError). (default: False)
2645 2652
2646 2653 ``tweakdefaults``
2647 2654
2648 2655 By default Mercurial's behavior changes very little from release
2649 2656 to release, but over time the recommended config settings
2650 2657 shift. Enable this config to opt in to get automatic tweaks to
2651 2658 Mercurial's behavior over time. This config setting will have no
2652 2659 effect if ``HGPLAIN`` is set or ``HGPLAINEXCEPT`` is set and does
2653 2660 not include ``tweakdefaults``. (default: False)
2654 2661
2655 2662 It currently means::
2656 2663
2657 2664 .. tweakdefaultsmarker
2658 2665
2659 2666 ``username``
2660 2667 The committer of a changeset created when running "commit".
2661 2668 Typically a person's name and email address, e.g. ``Fred Widget
2662 2669 <fred@example.com>``. Environment variables in the
2663 2670 username are expanded.
2664 2671
2665 2672 (default: ``$EMAIL`` or ``username@hostname``. If the username in
2666 2673 hgrc is empty, e.g. if the system admin set ``username =`` in the
2667 2674 system hgrc, it has to be specified manually or in a different
2668 2675 hgrc file)
2669 2676
2670 2677 ``verbose``
2671 2678 Increase the amount of output printed. (default: False)
2672 2679
2673 2680
2674 2681 ``command-templates``
2675 2682 ---------------------
2676 2683
2677 2684 Templates used for customizing the output of commands.
2678 2685
2679 2686 ``graphnode``
2680 2687 The template used to print changeset nodes in an ASCII revision graph.
2681 2688 (default: ``{graphnode}``)
2682 2689
2683 2690 ``log``
2684 2691 Template string for commands that print changesets.
2685 2692
2686 2693 ``mergemarker``
2687 2694 The template used to print the commit description next to each conflict
2688 2695 marker during merge conflicts. See :hg:`help templates` for the template
2689 2696 format.
2690 2697
2691 2698 Defaults to showing the hash, tags, branches, bookmarks, author, and
2692 2699 the first line of the commit description.
2693 2700
2694 2701 If you use non-ASCII characters in names for tags, branches, bookmarks,
2695 2702 authors, and/or commit descriptions, you must pay attention to encodings of
2696 2703 managed files. At template expansion, non-ASCII characters use the encoding
2697 2704 specified by the ``--encoding`` global option, ``HGENCODING`` or other
2698 2705 environment variables that govern your locale. If the encoding of the merge
2699 2706 markers is different from the encoding of the merged files,
2700 2707 serious problems may occur.
2701 2708
2702 2709 Can be overridden per-merge-tool, see the ``[merge-tools]`` section.
2703 2710
2704 2711 ``oneline-summary``
2705 2712 A template used by `hg rebase` and other commands for showing a one-line
2706 2713 summary of a commit. If the template configured here is longer than one
2707 2714 line, then only the first line is used.
2708 2715
2709 2716 The template can be overridden per command by defining a template in
2710 2717 `oneline-summary.<command>`, where `<command>` can be e.g. "rebase".
2711 2718
2712 2719 ``pre-merge-tool-output``
2713 2720 A template that is printed before executing an external merge tool. This can
2714 2721 be used to print out additional context that might be useful to have during
2715 2722 the conflict resolution, such as the description of the various commits
2716 2723 involved or bookmarks/tags.
2717 2724
2718 2725 Additional information is available in the ``local`, ``base``, and ``other``
2719 2726 dicts. For example: ``{local.label}``, ``{base.name}``, or
2720 2727 ``{other.islink}``.
2721 2728
2722 2729
2723 2730 ``web``
2724 2731 -------
2725 2732
2726 2733 Web interface configuration. The settings in this section apply to
2727 2734 both the builtin webserver (started by :hg:`serve`) and the script you
2728 2735 run through a webserver (``hgweb.cgi`` and the derivatives for FastCGI
2729 2736 and WSGI).
2730 2737
2731 2738 The Mercurial webserver does no authentication (it does not prompt for
2732 2739 usernames and passwords to validate *who* users are), but it does do
2733 2740 authorization (it grants or denies access for *authenticated users*
2734 2741 based on settings in this section). You must either configure your
2735 2742 webserver to do authentication for you, or disable the authorization
2736 2743 checks.
2737 2744
2738 2745 For a quick setup in a trusted environment, e.g., a private LAN, where
2739 2746 you want it to accept pushes from anybody, you can use the following
2740 2747 command line::
2741 2748
2742 2749 $ hg --config web.allow-push=* --config web.push_ssl=False serve
2743 2750
2744 2751 Note that this will allow anybody to push anything to the server and
2745 2752 that this should not be used for public servers.
2746 2753
2747 2754 The full set of options is:
2748 2755
2749 2756 ``accesslog``
2750 2757 Where to output the access log. (default: stdout)
2751 2758
2752 2759 ``address``
2753 2760 Interface address to bind to. (default: all)
2754 2761
2755 2762 ``allow-archive``
2756 2763 List of archive format (bz2, gz, zip) allowed for downloading.
2757 2764 (default: empty)
2758 2765
2759 2766 ``allowbz2``
2760 2767 (DEPRECATED) Whether to allow .tar.bz2 downloading of repository
2761 2768 revisions.
2762 2769 (default: False)
2763 2770
2764 2771 ``allowgz``
2765 2772 (DEPRECATED) Whether to allow .tar.gz downloading of repository
2766 2773 revisions.
2767 2774 (default: False)
2768 2775
2769 2776 ``allow-pull``
2770 2777 Whether to allow pulling from the repository. (default: True)
2771 2778
2772 2779 ``allow-push``
2773 2780 Whether to allow pushing to the repository. If empty or not set,
2774 2781 pushing is not allowed. If the special value ``*``, any remote
2775 2782 user can push, including unauthenticated users. Otherwise, the
2776 2783 remote user must have been authenticated, and the authenticated
2777 2784 user name must be present in this list. The contents of the
2778 2785 allow-push list are examined after the deny_push list.
2779 2786
2780 2787 ``allow_read``
2781 2788 If the user has not already been denied repository access due to
2782 2789 the contents of deny_read, this list determines whether to grant
2783 2790 repository access to the user. If this list is not empty, and the
2784 2791 user is unauthenticated or not present in the list, then access is
2785 2792 denied for the user. If the list is empty or not set, then access
2786 2793 is permitted to all users by default. Setting allow_read to the
2787 2794 special value ``*`` is equivalent to it not being set (i.e. access
2788 2795 is permitted to all users). The contents of the allow_read list are
2789 2796 examined after the deny_read list.
2790 2797
2791 2798 ``allowzip``
2792 2799 (DEPRECATED) Whether to allow .zip downloading of repository
2793 2800 revisions. This feature creates temporary files.
2794 2801 (default: False)
2795 2802
2796 2803 ``archivesubrepos``
2797 2804 Whether to recurse into subrepositories when archiving.
2798 2805 (default: False)
2799 2806
2800 2807 ``baseurl``
2801 2808 Base URL to use when publishing URLs in other locations, so
2802 2809 third-party tools like email notification hooks can construct
2803 2810 URLs. Example: ``http://hgserver/repos/``.
2804 2811
2805 2812 ``cacerts``
2806 2813 Path to file containing a list of PEM encoded certificate
2807 2814 authority certificates. Environment variables and ``~user``
2808 2815 constructs are expanded in the filename. If specified on the
2809 2816 client, then it will verify the identity of remote HTTPS servers
2810 2817 with these certificates.
2811 2818
2812 2819 To disable SSL verification temporarily, specify ``--insecure`` from
2813 2820 command line.
2814 2821
2815 2822 You can use OpenSSL's CA certificate file if your platform has
2816 2823 one. On most Linux systems this will be
2817 2824 ``/etc/ssl/certs/ca-certificates.crt``. Otherwise you will have to
2818 2825 generate this file manually. The form must be as follows::
2819 2826
2820 2827 -----BEGIN CERTIFICATE-----
2821 2828 ... (certificate in base64 PEM encoding) ...
2822 2829 -----END CERTIFICATE-----
2823 2830 -----BEGIN CERTIFICATE-----
2824 2831 ... (certificate in base64 PEM encoding) ...
2825 2832 -----END CERTIFICATE-----
2826 2833
2827 2834 ``cache``
2828 2835 Whether to support caching in hgweb. (default: True)
2829 2836
2830 2837 ``certificate``
2831 2838 Certificate to use when running :hg:`serve`.
2832 2839
2833 2840 ``collapse``
2834 2841 With ``descend`` enabled, repositories in subdirectories are shown at
2835 2842 a single level alongside repositories in the current path. With
2836 2843 ``collapse`` also enabled, repositories residing at a deeper level than
2837 2844 the current path are grouped behind navigable directory entries that
2838 2845 lead to the locations of these repositories. In effect, this setting
2839 2846 collapses each collection of repositories found within a subdirectory
2840 2847 into a single entry for that subdirectory. (default: False)
2841 2848
2842 2849 ``comparisoncontext``
2843 2850 Number of lines of context to show in side-by-side file comparison. If
2844 2851 negative or the value ``full``, whole files are shown. (default: 5)
2845 2852
2846 2853 This setting can be overridden by a ``context`` request parameter to the
2847 2854 ``comparison`` command, taking the same values.
2848 2855
2849 2856 ``contact``
2850 2857 Name or email address of the person in charge of the repository.
2851 2858 (default: ui.username or ``$EMAIL`` or "unknown" if unset or empty)
2852 2859
2853 2860 ``csp``
2854 2861 Send a ``Content-Security-Policy`` HTTP header with this value.
2855 2862
2856 2863 The value may contain a special string ``%nonce%``, which will be replaced
2857 2864 by a randomly-generated one-time use value. If the value contains
2858 2865 ``%nonce%``, ``web.cache`` will be disabled, as caching undermines the
2859 2866 one-time property of the nonce. This nonce will also be inserted into
2860 2867 ``<script>`` elements containing inline JavaScript.
2861 2868
2862 2869 Note: lots of HTML content sent by the server is derived from repository
2863 2870 data. Please consider the potential for malicious repository data to
2864 2871 "inject" itself into generated HTML content as part of your security
2865 2872 threat model.
2866 2873
2867 2874 ``deny_push``
2868 2875 Whether to deny pushing to the repository. If empty or not set,
2869 2876 push is not denied. If the special value ``*``, all remote users are
2870 2877 denied push. Otherwise, unauthenticated users are all denied, and
2871 2878 any authenticated user name present in this list is also denied. The
2872 2879 contents of the deny_push list are examined before the allow-push list.
2873 2880
2874 2881 ``deny_read``
2875 2882 Whether to deny reading/viewing of the repository. If this list is
2876 2883 not empty, unauthenticated users are all denied, and any
2877 2884 authenticated user name present in this list is also denied access to
2878 2885 the repository. If set to the special value ``*``, all remote users
2879 2886 are denied access (rarely needed ;). If deny_read is empty or not set,
2880 2887 the determination of repository access depends on the presence and
2881 2888 content of the allow_read list (see description). If both
2882 2889 deny_read and allow_read are empty or not set, then access is
2883 2890 permitted to all users by default. If the repository is being
2884 2891 served via hgwebdir, denied users will not be able to see it in
2885 2892 the list of repositories. The contents of the deny_read list have
2886 2893 priority over (are examined before) the contents of the allow_read
2887 2894 list.
2888 2895
2889 2896 ``descend``
2890 2897 hgwebdir indexes will not descend into subdirectories. Only repositories
2891 2898 directly in the current path will be shown (other repositories are still
2892 2899 available from the index corresponding to their containing path).
2893 2900
2894 2901 ``description``
2895 2902 Textual description of the repository's purpose or contents.
2896 2903 (default: "unknown")
2897 2904
2898 2905 ``encoding``
2899 2906 Character encoding name. (default: the current locale charset)
2900 2907 Example: "UTF-8".
2901 2908
2902 2909 ``errorlog``
2903 2910 Where to output the error log. (default: stderr)
2904 2911
2905 2912 ``guessmime``
2906 2913 Control MIME types for raw download of file content.
2907 2914 Set to True to let hgweb guess the content type from the file
2908 2915 extension. This will serve HTML files as ``text/html`` and might
2909 2916 allow cross-site scripting attacks when serving untrusted
2910 2917 repositories. (default: False)
2911 2918
2912 2919 ``hidden``
2913 2920 Whether to hide the repository in the hgwebdir index.
2914 2921 (default: False)
2915 2922
2916 2923 ``ipv6``
2917 2924 Whether to use IPv6. (default: False)
2918 2925
2919 2926 ``labels``
2920 2927 List of string *labels* associated with the repository.
2921 2928
2922 2929 Labels are exposed as a template keyword and can be used to customize
2923 2930 output. e.g. the ``index`` template can group or filter repositories
2924 2931 by labels and the ``summary`` template can display additional content
2925 2932 if a specific label is present.
2926 2933
2927 2934 ``logoimg``
2928 2935 File name of the logo image that some templates display on each page.
2929 2936 The file name is relative to ``staticurl``. That is, the full path to
2930 2937 the logo image is "staticurl/logoimg".
2931 2938 If unset, ``hglogo.png`` will be used.
2932 2939
2933 2940 ``logourl``
2934 2941 Base URL to use for logos. If unset, ``https://mercurial-scm.org/``
2935 2942 will be used.
2936 2943
2937 2944 ``maxchanges``
2938 2945 Maximum number of changes to list on the changelog. (default: 10)
2939 2946
2940 2947 ``maxfiles``
2941 2948 Maximum number of files to list per changeset. (default: 10)
2942 2949
2943 2950 ``maxshortchanges``
2944 2951 Maximum number of changes to list on the shortlog, graph or filelog
2945 2952 pages. (default: 60)
2946 2953
2947 2954 ``name``
2948 2955 Repository name to use in the web interface.
2949 2956 (default: current working directory)
2950 2957
2951 2958 ``port``
2952 2959 Port to listen on. (default: 8000)
2953 2960
2954 2961 ``prefix``
2955 2962 Prefix path to serve from. (default: '' (server root))
2956 2963
2957 2964 ``push_ssl``
2958 2965 Whether to require that inbound pushes be transported over SSL to
2959 2966 prevent password sniffing. (default: True)
2960 2967
2961 2968 ``refreshinterval``
2962 2969 How frequently directory listings re-scan the filesystem for new
2963 2970 repositories, in seconds. This is relevant when wildcards are used
2964 2971 to define paths. Depending on how much filesystem traversal is
2965 2972 required, refreshing may negatively impact performance.
2966 2973
2967 2974 Values less than or equal to 0 always refresh.
2968 2975 (default: 20)
2969 2976
2970 2977 ``server-header``
2971 2978 Value for HTTP ``Server`` response header.
2972 2979
2973 2980 ``static``
2974 2981 Directory where static files are served from.
2975 2982
2976 2983 ``staticurl``
2977 2984 Base URL to use for static files. If unset, static files (e.g. the
2978 2985 hgicon.png favicon) will be served by the CGI script itself. Use
2979 2986 this setting to serve them directly with the HTTP server.
2980 2987 Example: ``http://hgserver/static/``.
2981 2988
2982 2989 ``stripes``
2983 2990 How many lines a "zebra stripe" should span in multi-line output.
2984 2991 Set to 0 to disable. (default: 1)
2985 2992
2986 2993 ``style``
2987 2994 Which template map style to use. The available options are the names of
2988 2995 subdirectories in the HTML templates path. (default: ``paper``)
2989 2996 Example: ``monoblue``.
2990 2997
2991 2998 ``templates``
2992 2999 Where to find the HTML templates. The default path to the HTML templates
2993 3000 can be obtained from ``hg debuginstall``.
2994 3001
2995 3002 ``websub``
2996 3003 ----------
2997 3004
2998 3005 Web substitution filter definition. You can use this section to
2999 3006 define a set of regular expression substitution patterns which
3000 3007 let you automatically modify the hgweb server output.
3001 3008
3002 3009 The default hgweb templates only apply these substitution patterns
3003 3010 on the revision description fields. You can apply them anywhere
3004 3011 you want when you create your own templates by adding calls to the
3005 3012 "websub" filter (usually after calling the "escape" filter).
3006 3013
3007 3014 This can be used, for example, to convert issue references to links
3008 3015 to your issue tracker, or to convert "markdown-like" syntax into
3009 3016 HTML (see the examples below).
3010 3017
3011 3018 Each entry in this section names a substitution filter.
3012 3019 The value of each entry defines the substitution expression itself.
3013 3020 The websub expressions follow the old interhg extension syntax,
3014 3021 which in turn imitates the Unix sed replacement syntax::
3015 3022
3016 3023 patternname = s/SEARCH_REGEX/REPLACE_EXPRESSION/[i]
3017 3024
3018 3025 You can use any separator other than "/". The final "i" is optional
3019 3026 and indicates that the search must be case insensitive.
3020 3027
3021 3028 Examples::
3022 3029
3023 3030 [websub]
3024 3031 issues = s|issue(\d+)|<a href="http://bts.example.org/issue\1">issue\1</a>|i
3025 3032 italic = s/\b_(\S+)_\b/<i>\1<\/i>/
3026 3033 bold = s/\*\b(\S+)\b\*/<b>\1<\/b>/
3027 3034
3028 3035 ``worker``
3029 3036 ----------
3030 3037
3031 3038 Parallel master/worker configuration. We currently perform working
3032 3039 directory updates in parallel on Unix-like systems, which greatly
3033 3040 helps performance.
3034 3041
3035 3042 ``enabled``
3036 3043 Whether to enable workers code to be used.
3037 3044 (default: true)
3038 3045
3039 3046 ``numcpus``
3040 3047 Number of CPUs to use for parallel operations. A zero or
3041 3048 negative value is treated as ``use the default``.
3042 3049 (default: 4 or the number of CPUs on the system, whichever is larger)
3043 3050
3044 3051 ``backgroundclose``
3045 3052 Whether to enable closing file handles on background threads during certain
3046 3053 operations. Some platforms aren't very efficient at closing file
3047 3054 handles that have been written or appended to. By performing file closing
3048 3055 on background threads, file write rate can increase substantially.
3049 3056 (default: true on Windows, false elsewhere)
3050 3057
3051 3058 ``backgroundcloseminfilecount``
3052 3059 Minimum number of files required to trigger background file closing.
3053 3060 Operations not writing this many files won't start background close
3054 3061 threads.
3055 3062 (default: 2048)
3056 3063
3057 3064 ``backgroundclosemaxqueue``
3058 3065 The maximum number of opened file handles waiting to be closed in the
3059 3066 background. This option only has an effect if ``backgroundclose`` is
3060 3067 enabled.
3061 3068 (default: 384)
3062 3069
3063 3070 ``backgroundclosethreadcount``
3064 3071 Number of threads to process background file closes. Only relevant if
3065 3072 ``backgroundclose`` is enabled.
3066 3073 (default: 4)
@@ -1,350 +1,362 b''
1 1 # hook.py - hook support for mercurial
2 2 #
3 3 # Copyright 2007 Matt Mackall <mpm@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 absolute_import
9 9
10 10 import contextlib
11 11 import errno
12 12 import os
13 13 import sys
14 14
15 15 from .i18n import _
16 16 from .pycompat import getattr
17 17 from . import (
18 18 demandimport,
19 19 encoding,
20 20 error,
21 21 extensions,
22 22 pycompat,
23 23 util,
24 24 )
25 25 from .utils import (
26 26 procutil,
27 27 resourceutil,
28 28 stringutil,
29 29 )
30 30
31 31
32 32 def pythonhook(ui, repo, htype, hname, funcname, args, throw):
33 33 """call python hook. hook is callable object, looked up as
34 34 name in python module. if callable returns "true", hook
35 35 fails, else passes. if hook raises exception, treated as
36 36 hook failure. exception propagates if throw is "true".
37 37
38 38 reason for "true" meaning "hook failed" is so that
39 39 unmodified commands (e.g. mercurial.commands.update) can
40 40 be run as hooks without wrappers to convert return values."""
41 41
42 42 if callable(funcname):
43 43 obj = funcname
44 44 funcname = pycompat.sysbytes(obj.__module__ + "." + obj.__name__)
45 45 else:
46 46 d = funcname.rfind(b'.')
47 47 if d == -1:
48 48 raise error.HookLoadError(
49 49 _(b'%s hook is invalid: "%s" not in a module')
50 50 % (hname, funcname)
51 51 )
52 52 modname = funcname[:d]
53 53 oldpaths = sys.path
54 54 if resourceutil.mainfrozen():
55 55 # binary installs require sys.path manipulation
56 56 modpath, modfile = os.path.split(modname)
57 57 if modpath and modfile:
58 58 sys.path = sys.path[:] + [modpath]
59 59 modname = modfile
60 60 with demandimport.deactivated():
61 61 try:
62 62 obj = __import__(pycompat.sysstr(modname))
63 63 except (ImportError, SyntaxError):
64 64 e1 = sys.exc_info()
65 65 try:
66 66 # extensions are loaded with hgext_ prefix
67 67 obj = __import__("hgext_%s" % pycompat.sysstr(modname))
68 68 except (ImportError, SyntaxError):
69 69 e2 = sys.exc_info()
70 70 if ui.tracebackflag:
71 71 ui.warn(
72 72 _(
73 73 b'exception from first failed import '
74 74 b'attempt:\n'
75 75 )
76 76 )
77 77 ui.traceback(e1)
78 78 if ui.tracebackflag:
79 79 ui.warn(
80 80 _(
81 81 b'exception from second failed import '
82 82 b'attempt:\n'
83 83 )
84 84 )
85 85 ui.traceback(e2)
86 86
87 87 if not ui.tracebackflag:
88 88 tracebackhint = _(
89 89 b'run with --traceback for stack trace'
90 90 )
91 91 else:
92 92 tracebackhint = None
93 93 raise error.HookLoadError(
94 94 _(b'%s hook is invalid: import of "%s" failed')
95 95 % (hname, modname),
96 96 hint=tracebackhint,
97 97 )
98 98 sys.path = oldpaths
99 99 try:
100 100 for p in funcname.split(b'.')[1:]:
101 101 obj = getattr(obj, p)
102 102 except AttributeError:
103 103 raise error.HookLoadError(
104 104 _(b'%s hook is invalid: "%s" is not defined')
105 105 % (hname, funcname)
106 106 )
107 107 if not callable(obj):
108 108 raise error.HookLoadError(
109 109 _(b'%s hook is invalid: "%s" is not callable')
110 110 % (hname, funcname)
111 111 )
112 112
113 113 ui.note(_(b"calling hook %s: %s\n") % (hname, funcname))
114 114 starttime = util.timer()
115 115
116 116 try:
117 117 r = obj(ui=ui, repo=repo, hooktype=htype, **pycompat.strkwargs(args))
118 118 except Exception as exc:
119 119 if isinstance(exc, error.Abort):
120 120 ui.warn(_(b'error: %s hook failed: %s\n') % (hname, exc.args[0]))
121 121 else:
122 122 ui.warn(
123 123 _(b'error: %s hook raised an exception: %s\n')
124 124 % (hname, stringutil.forcebytestr(exc))
125 125 )
126 126 if throw:
127 127 raise
128 128 if not ui.tracebackflag:
129 129 ui.warn(_(b'(run with --traceback for stack trace)\n'))
130 130 ui.traceback()
131 131 return True, True
132 132 finally:
133 133 duration = util.timer() - starttime
134 134 ui.log(
135 135 b'pythonhook',
136 136 b'pythonhook-%s: %s finished in %0.2f seconds\n',
137 137 htype,
138 138 funcname,
139 139 duration,
140 140 )
141 141 if r:
142 142 if throw:
143 143 raise error.HookAbort(_(b'%s hook failed') % hname)
144 144 ui.warn(_(b'warning: %s hook failed\n') % hname)
145 145 return r, False
146 146
147 147
148 148 def _exthook(ui, repo, htype, name, cmd, args, throw):
149 149 starttime = util.timer()
150 150 env = {}
151 151
152 152 # make in-memory changes visible to external process
153 153 if repo is not None:
154 154 tr = repo.currenttransaction()
155 155 repo.dirstate.write(tr)
156 156 if tr and tr.writepending():
157 157 env[b'HG_PENDING'] = repo.root
158 158 env[b'HG_HOOKTYPE'] = htype
159 159 env[b'HG_HOOKNAME'] = name
160 env[b'HGPLAIN'] = b'1'
160
161 if ui.config(b'hooks', b'%s:run-with-plain' % name) == b'auto':
162 plain = ui.plain()
163 else:
164 plain = ui.configbool(b'hooks', b'%s:run-with-plain' % name)
165 if plain:
166 env[b'HGPLAIN'] = b'1'
167 else:
168 env[b'HGPLAIN'] = b''
161 169
162 170 for k, v in pycompat.iteritems(args):
163 171 # transaction changes can accumulate MBs of data, so skip it
164 172 # for external hooks
165 173 if k == b'changes':
166 174 continue
167 175 if callable(v):
168 176 v = v()
169 177 if isinstance(v, (dict, list)):
170 178 v = stringutil.pprint(v)
171 179 env[b'HG_' + k.upper()] = v
172 180
173 181 if ui.configbool(b'hooks', b'tonative.%s' % name, False):
174 182 oldcmd = cmd
175 183 cmd = procutil.shelltonative(cmd, env)
176 184 if cmd != oldcmd:
177 185 ui.note(_(b'converting hook "%s" to native\n') % name)
178 186
179 187 ui.note(_(b"running hook %s: %s\n") % (name, cmd))
180 188
181 189 if repo:
182 190 cwd = repo.root
183 191 else:
184 192 cwd = encoding.getcwd()
185 193 r = ui.system(cmd, environ=env, cwd=cwd, blockedtag=b'exthook-%s' % (name,))
186 194
187 195 duration = util.timer() - starttime
188 196 ui.log(
189 197 b'exthook',
190 198 b'exthook-%s: %s finished in %0.2f seconds\n',
191 199 name,
192 200 cmd,
193 201 duration,
194 202 )
195 203 if r:
196 204 desc = procutil.explainexit(r)
197 205 if throw:
198 206 raise error.HookAbort(_(b'%s hook %s') % (name, desc))
199 207 ui.warn(_(b'warning: %s hook %s\n') % (name, desc))
200 208 return r
201 209
202 210
203 211 # represent an untrusted hook command
204 212 _fromuntrusted = object()
205 213
206 214
207 215 def _allhooks(ui):
208 216 """return a list of (hook-id, cmd) pairs sorted by priority"""
209 217 hooks = _hookitems(ui)
210 218 # Be careful in this section, propagating the real commands from untrusted
211 219 # sources would create a security vulnerability, make sure anything altered
212 220 # in that section uses "_fromuntrusted" as its command.
213 221 untrustedhooks = _hookitems(ui, _untrusted=True)
214 222 for name, value in untrustedhooks.items():
215 223 trustedvalue = hooks.get(name, ((), (), name, _fromuntrusted))
216 224 if value != trustedvalue:
217 225 (lp, lo, lk, lv) = trustedvalue
218 226 hooks[name] = (lp, lo, lk, _fromuntrusted)
219 227 # (end of the security sensitive section)
220 228 return [(k, v) for p, o, k, v in sorted(hooks.values())]
221 229
222 230
223 231 def _hookitems(ui, _untrusted=False):
224 232 """return all hooks items ready to be sorted"""
225 233 hooks = {}
226 234 for name, cmd in ui.configitems(b'hooks', untrusted=_untrusted):
227 if name.startswith(b'priority.') or name.startswith(b'tonative.'):
235 if (
236 name.startswith(b'priority.')
237 or name.startswith(b'tonative.')
238 or b':' in name
239 ):
228 240 continue
229 241
230 242 priority = ui.configint(b'hooks', b'priority.%s' % name, 0)
231 243 hooks[name] = ((-priority,), (len(hooks),), name, cmd)
232 244 return hooks
233 245
234 246
235 247 _redirect = False
236 248
237 249
238 250 def redirect(state):
239 251 global _redirect
240 252 _redirect = state
241 253
242 254
243 255 def hashook(ui, htype):
244 256 """return True if a hook is configured for 'htype'"""
245 257 if not ui.callhooks:
246 258 return False
247 259 for hname, cmd in _allhooks(ui):
248 260 if hname.split(b'.')[0] == htype and cmd:
249 261 return True
250 262 return False
251 263
252 264
253 265 def hook(ui, repo, htype, throw=False, **args):
254 266 if not ui.callhooks:
255 267 return False
256 268
257 269 hooks = []
258 270 for hname, cmd in _allhooks(ui):
259 271 if hname.split(b'.')[0] == htype and cmd:
260 272 hooks.append((hname, cmd))
261 273
262 274 res = runhooks(ui, repo, htype, hooks, throw=throw, **args)
263 275 r = False
264 276 for hname, cmd in hooks:
265 277 r = res[hname][0] or r
266 278 return r
267 279
268 280
269 281 @contextlib.contextmanager
270 282 def redirect_stdio():
271 283 """Redirects stdout to stderr, if possible."""
272 284
273 285 oldstdout = -1
274 286 try:
275 287 if _redirect:
276 288 try:
277 289 stdoutno = procutil.stdout.fileno()
278 290 stderrno = procutil.stderr.fileno()
279 291 # temporarily redirect stdout to stderr, if possible
280 292 if stdoutno >= 0 and stderrno >= 0:
281 293 procutil.stdout.flush()
282 294 oldstdout = os.dup(stdoutno)
283 295 os.dup2(stderrno, stdoutno)
284 296 except (OSError, AttributeError):
285 297 # files seem to be bogus, give up on redirecting (WSGI, etc)
286 298 pass
287 299
288 300 yield
289 301
290 302 finally:
291 303 # The stderr is fully buffered on Windows when connected to a pipe.
292 304 # A forcible flush is required to make small stderr data in the
293 305 # remote side available to the client immediately.
294 306 try:
295 307 procutil.stderr.flush()
296 308 except IOError as err:
297 309 if err.errno not in (errno.EPIPE, errno.EIO, errno.EBADF):
298 310 raise error.StdioError(err)
299 311
300 312 if _redirect and oldstdout >= 0:
301 313 try:
302 314 procutil.stdout.flush() # write hook output to stderr fd
303 315 except IOError as err:
304 316 if err.errno not in (errno.EPIPE, errno.EIO, errno.EBADF):
305 317 raise error.StdioError(err)
306 318 os.dup2(oldstdout, stdoutno)
307 319 os.close(oldstdout)
308 320
309 321
310 322 def runhooks(ui, repo, htype, hooks, throw=False, **args):
311 323 args = pycompat.byteskwargs(args)
312 324 res = {}
313 325
314 326 with redirect_stdio():
315 327 for hname, cmd in hooks:
316 328 if cmd is _fromuntrusted:
317 329 if throw:
318 330 raise error.HookAbort(
319 331 _(b'untrusted hook %s not executed') % hname,
320 332 hint=_(b"see 'hg help config.trusted'"),
321 333 )
322 334 ui.warn(_(b'warning: untrusted hook %s not executed\n') % hname)
323 335 r = 1
324 336 raised = False
325 337 elif callable(cmd):
326 338 r, raised = pythonhook(ui, repo, htype, hname, cmd, args, throw)
327 339 elif cmd.startswith(b'python:'):
328 340 if cmd.count(b':') >= 2:
329 341 path, cmd = cmd[7:].rsplit(b':', 1)
330 342 path = util.expandpath(path)
331 343 if repo:
332 344 path = os.path.join(repo.root, path)
333 345 try:
334 346 mod = extensions.loadpath(path, b'hghook.%s' % hname)
335 347 except Exception:
336 348 ui.write(_(b"loading %s hook failed:\n") % hname)
337 349 raise
338 350 hookfn = getattr(mod, cmd)
339 351 else:
340 352 hookfn = cmd[7:].strip()
341 353 r, raised = pythonhook(
342 354 ui, repo, htype, hname, hookfn, args, throw
343 355 )
344 356 else:
345 357 r = _exthook(ui, repo, htype, hname, cmd, args, throw)
346 358 raised = False
347 359
348 360 res[hname] = r, raised
349 361
350 362 return res
@@ -1,2393 +1,2391 b''
1 1 # ui.py - user interface bits for mercurial
2 2 #
3 3 # Copyright 2005-2007 Matt Mackall <mpm@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 absolute_import
9 9
10 10 import collections
11 11 import contextlib
12 12 import datetime
13 13 import errno
14 14 import getpass
15 15 import inspect
16 16 import os
17 17 import re
18 18 import signal
19 19 import socket
20 20 import subprocess
21 21 import sys
22 22 import traceback
23 23
24 24 from .i18n import _
25 25 from .node import hex
26 26 from .pycompat import (
27 27 getattr,
28 28 open,
29 29 setattr,
30 30 )
31 31
32 32 from . import (
33 33 color,
34 34 config,
35 35 configitems,
36 36 encoding,
37 37 error,
38 38 formatter,
39 39 loggingutil,
40 40 progress,
41 41 pycompat,
42 42 rcutil,
43 43 scmutil,
44 44 util,
45 45 )
46 46 from .utils import (
47 47 dateutil,
48 48 procutil,
49 49 resourceutil,
50 50 stringutil,
51 51 )
52 52
53 53 urlreq = util.urlreq
54 54
55 55 # for use with str.translate(None, _keepalnum), to keep just alphanumerics
56 56 _keepalnum = b''.join(
57 57 c for c in map(pycompat.bytechr, range(256)) if not c.isalnum()
58 58 )
59 59
60 60 # The config knobs that will be altered (if unset) by ui.tweakdefaults.
61 61 tweakrc = b"""
62 62 [ui]
63 # Gives detailed exit codes for input/user errors, config errors, etc.
64 detailed-exit-code = True
65 63 # The rollback command is dangerous. As a rule, don't use it.
66 64 rollback = False
67 65 # Make `hg status` report copy information
68 66 statuscopies = yes
69 67 # Prefer curses UIs when available. Revert to plain-text with `text`.
70 68 interface = curses
71 69 # Make compatible commands emit cwd-relative paths by default.
72 70 relative-paths = yes
73 71
74 72 [commands]
75 73 # Grep working directory by default.
76 74 grep.all-files = True
77 75 # Refuse to perform an `hg update` that would cause a file content merge
78 76 update.check = noconflict
79 77 # Show conflicts information in `hg status`
80 78 status.verbose = True
81 79 # Make `hg resolve` with no action (like `-m`) fail instead of re-merging.
82 80 resolve.explicit-re-merge = True
83 81
84 82 [diff]
85 83 git = 1
86 84 showfunc = 1
87 85 word-diff = 1
88 86 """
89 87
90 88 samplehgrcs = {
91 89 b'user': b"""# example user config (see 'hg help config' for more info)
92 90 [ui]
93 91 # name and email, e.g.
94 92 # username = Jane Doe <jdoe@example.com>
95 93 username =
96 94
97 95 # We recommend enabling tweakdefaults to get slight improvements to
98 96 # the UI over time. Make sure to set HGPLAIN in the environment when
99 97 # writing scripts!
100 98 # tweakdefaults = True
101 99
102 100 # uncomment to disable color in command output
103 101 # (see 'hg help color' for details)
104 102 # color = never
105 103
106 104 # uncomment to disable command output pagination
107 105 # (see 'hg help pager' for details)
108 106 # paginate = never
109 107
110 108 [extensions]
111 109 # uncomment the lines below to enable some popular extensions
112 110 # (see 'hg help extensions' for more info)
113 111 #
114 112 # histedit =
115 113 # rebase =
116 114 # uncommit =
117 115 """,
118 116 b'cloned': b"""# example repository config (see 'hg help config' for more info)
119 117 [paths]
120 118 default = %s
121 119
122 120 # path aliases to other clones of this repo in URLs or filesystem paths
123 121 # (see 'hg help config.paths' for more info)
124 122 #
125 123 # default:pushurl = ssh://jdoe@example.net/hg/jdoes-fork
126 124 # my-fork = ssh://jdoe@example.net/hg/jdoes-fork
127 125 # my-clone = /home/jdoe/jdoes-clone
128 126
129 127 [ui]
130 128 # name and email (local to this repository, optional), e.g.
131 129 # username = Jane Doe <jdoe@example.com>
132 130 """,
133 131 b'local': b"""# example repository config (see 'hg help config' for more info)
134 132 [paths]
135 133 # path aliases to other clones of this repo in URLs or filesystem paths
136 134 # (see 'hg help config.paths' for more info)
137 135 #
138 136 # default = http://example.com/hg/example-repo
139 137 # default:pushurl = ssh://jdoe@example.net/hg/jdoes-fork
140 138 # my-fork = ssh://jdoe@example.net/hg/jdoes-fork
141 139 # my-clone = /home/jdoe/jdoes-clone
142 140
143 141 [ui]
144 142 # name and email (local to this repository, optional), e.g.
145 143 # username = Jane Doe <jdoe@example.com>
146 144 """,
147 145 b'global': b"""# example system-wide hg config (see 'hg help config' for more info)
148 146
149 147 [ui]
150 148 # uncomment to disable color in command output
151 149 # (see 'hg help color' for details)
152 150 # color = never
153 151
154 152 # uncomment to disable command output pagination
155 153 # (see 'hg help pager' for details)
156 154 # paginate = never
157 155
158 156 [extensions]
159 157 # uncomment the lines below to enable some popular extensions
160 158 # (see 'hg help extensions' for more info)
161 159 #
162 160 # blackbox =
163 161 # churn =
164 162 """,
165 163 }
166 164
167 165
168 166 def _maybestrurl(maybebytes):
169 167 return pycompat.rapply(pycompat.strurl, maybebytes)
170 168
171 169
172 170 def _maybebytesurl(maybestr):
173 171 return pycompat.rapply(pycompat.bytesurl, maybestr)
174 172
175 173
176 174 class httppasswordmgrdbproxy(object):
177 175 """Delays loading urllib2 until it's needed."""
178 176
179 177 def __init__(self):
180 178 self._mgr = None
181 179
182 180 def _get_mgr(self):
183 181 if self._mgr is None:
184 182 self._mgr = urlreq.httppasswordmgrwithdefaultrealm()
185 183 return self._mgr
186 184
187 185 def add_password(self, realm, uris, user, passwd):
188 186 return self._get_mgr().add_password(
189 187 _maybestrurl(realm),
190 188 _maybestrurl(uris),
191 189 _maybestrurl(user),
192 190 _maybestrurl(passwd),
193 191 )
194 192
195 193 def find_user_password(self, realm, uri):
196 194 mgr = self._get_mgr()
197 195 return _maybebytesurl(
198 196 mgr.find_user_password(_maybestrurl(realm), _maybestrurl(uri))
199 197 )
200 198
201 199
202 200 def _catchterm(*args):
203 201 raise error.SignalInterrupt
204 202
205 203
206 204 # unique object used to detect no default value has been provided when
207 205 # retrieving configuration value.
208 206 _unset = object()
209 207
210 208 # _reqexithandlers: callbacks run at the end of a request
211 209 _reqexithandlers = []
212 210
213 211
214 212 class ui(object):
215 213 def __init__(self, src=None):
216 214 """Create a fresh new ui object if no src given
217 215
218 216 Use uimod.ui.load() to create a ui which knows global and user configs.
219 217 In most cases, you should use ui.copy() to create a copy of an existing
220 218 ui object.
221 219 """
222 220 # _buffers: used for temporary capture of output
223 221 self._buffers = []
224 222 # 3-tuple describing how each buffer in the stack behaves.
225 223 # Values are (capture stderr, capture subprocesses, apply labels).
226 224 self._bufferstates = []
227 225 # When a buffer is active, defines whether we are expanding labels.
228 226 # This exists to prevent an extra list lookup.
229 227 self._bufferapplylabels = None
230 228 self.quiet = self.verbose = self.debugflag = self.tracebackflag = False
231 229 self._reportuntrusted = True
232 230 self._knownconfig = configitems.coreitems
233 231 self._ocfg = config.config() # overlay
234 232 self._tcfg = config.config() # trusted
235 233 self._ucfg = config.config() # untrusted
236 234 self._trustusers = set()
237 235 self._trustgroups = set()
238 236 self.callhooks = True
239 237 # Insecure server connections requested.
240 238 self.insecureconnections = False
241 239 # Blocked time
242 240 self.logblockedtimes = False
243 241 # color mode: see mercurial/color.py for possible value
244 242 self._colormode = None
245 243 self._terminfoparams = {}
246 244 self._styles = {}
247 245 self._uninterruptible = False
248 246 self.showtimestamp = False
249 247
250 248 if src:
251 249 self._fout = src._fout
252 250 self._ferr = src._ferr
253 251 self._fin = src._fin
254 252 self._fmsg = src._fmsg
255 253 self._fmsgout = src._fmsgout
256 254 self._fmsgerr = src._fmsgerr
257 255 self._finoutredirected = src._finoutredirected
258 256 self._loggers = src._loggers.copy()
259 257 self.pageractive = src.pageractive
260 258 self._disablepager = src._disablepager
261 259 self._tweaked = src._tweaked
262 260
263 261 self._tcfg = src._tcfg.copy()
264 262 self._ucfg = src._ucfg.copy()
265 263 self._ocfg = src._ocfg.copy()
266 264 self._trustusers = src._trustusers.copy()
267 265 self._trustgroups = src._trustgroups.copy()
268 266 self.environ = src.environ
269 267 self.callhooks = src.callhooks
270 268 self.insecureconnections = src.insecureconnections
271 269 self._colormode = src._colormode
272 270 self._terminfoparams = src._terminfoparams.copy()
273 271 self._styles = src._styles.copy()
274 272
275 273 self.fixconfig()
276 274
277 275 self.httppasswordmgrdb = src.httppasswordmgrdb
278 276 self._blockedtimes = src._blockedtimes
279 277 else:
280 278 self._fout = procutil.stdout
281 279 self._ferr = procutil.stderr
282 280 self._fin = procutil.stdin
283 281 self._fmsg = None
284 282 self._fmsgout = self.fout # configurable
285 283 self._fmsgerr = self.ferr # configurable
286 284 self._finoutredirected = False
287 285 self._loggers = {}
288 286 self.pageractive = False
289 287 self._disablepager = False
290 288 self._tweaked = False
291 289
292 290 # shared read-only environment
293 291 self.environ = encoding.environ
294 292
295 293 self.httppasswordmgrdb = httppasswordmgrdbproxy()
296 294 self._blockedtimes = collections.defaultdict(int)
297 295
298 296 allowed = self.configlist(b'experimental', b'exportableenviron')
299 297 if b'*' in allowed:
300 298 self._exportableenviron = self.environ
301 299 else:
302 300 self._exportableenviron = {}
303 301 for k in allowed:
304 302 if k in self.environ:
305 303 self._exportableenviron[k] = self.environ[k]
306 304
307 305 @classmethod
308 306 def load(cls):
309 307 """Create a ui and load global and user configs"""
310 308 u = cls()
311 309 # we always trust global config files and environment variables
312 310 for t, f in rcutil.rccomponents():
313 311 if t == b'path':
314 312 u.readconfig(f, trust=True)
315 313 elif t == b'resource':
316 314 u.read_resource_config(f, trust=True)
317 315 elif t == b'items':
318 316 sections = set()
319 317 for section, name, value, source in f:
320 318 # do not set u._ocfg
321 319 # XXX clean this up once immutable config object is a thing
322 320 u._tcfg.set(section, name, value, source)
323 321 u._ucfg.set(section, name, value, source)
324 322 sections.add(section)
325 323 for section in sections:
326 324 u.fixconfig(section=section)
327 325 else:
328 326 raise error.ProgrammingError(b'unknown rctype: %s' % t)
329 327 u._maybetweakdefaults()
330 328 return u
331 329
332 330 def _maybetweakdefaults(self):
333 331 if not self.configbool(b'ui', b'tweakdefaults'):
334 332 return
335 333 if self._tweaked or self.plain(b'tweakdefaults'):
336 334 return
337 335
338 336 # Note: it is SUPER IMPORTANT that you set self._tweaked to
339 337 # True *before* any calls to setconfig(), otherwise you'll get
340 338 # infinite recursion between setconfig and this method.
341 339 #
342 340 # TODO: We should extract an inner method in setconfig() to
343 341 # avoid this weirdness.
344 342 self._tweaked = True
345 343 tmpcfg = config.config()
346 344 tmpcfg.parse(b'<tweakdefaults>', tweakrc)
347 345 for section in tmpcfg:
348 346 for name, value in tmpcfg.items(section):
349 347 if not self.hasconfig(section, name):
350 348 self.setconfig(section, name, value, b"<tweakdefaults>")
351 349
352 350 def copy(self):
353 351 return self.__class__(self)
354 352
355 353 def resetstate(self):
356 354 """Clear internal state that shouldn't persist across commands"""
357 355 if self._progbar:
358 356 self._progbar.resetstate() # reset last-print time of progress bar
359 357 self.httppasswordmgrdb = httppasswordmgrdbproxy()
360 358
361 359 @contextlib.contextmanager
362 360 def timeblockedsection(self, key):
363 361 # this is open-coded below - search for timeblockedsection to find them
364 362 starttime = util.timer()
365 363 try:
366 364 yield
367 365 finally:
368 366 self._blockedtimes[key + b'_blocked'] += (
369 367 util.timer() - starttime
370 368 ) * 1000
371 369
372 370 @contextlib.contextmanager
373 371 def uninterruptible(self):
374 372 """Mark an operation as unsafe.
375 373
376 374 Most operations on a repository are safe to interrupt, but a
377 375 few are risky (for example repair.strip). This context manager
378 376 lets you advise Mercurial that something risky is happening so
379 377 that control-C etc can be blocked if desired.
380 378 """
381 379 enabled = self.configbool(b'experimental', b'nointerrupt')
382 380 if enabled and self.configbool(
383 381 b'experimental', b'nointerrupt-interactiveonly'
384 382 ):
385 383 enabled = self.interactive()
386 384 if self._uninterruptible or not enabled:
387 385 # if nointerrupt support is turned off, the process isn't
388 386 # interactive, or we're already in an uninterruptible
389 387 # block, do nothing.
390 388 yield
391 389 return
392 390
393 391 def warn():
394 392 self.warn(_(b"shutting down cleanly\n"))
395 393 self.warn(
396 394 _(b"press ^C again to terminate immediately (dangerous)\n")
397 395 )
398 396 return True
399 397
400 398 with procutil.uninterruptible(warn):
401 399 try:
402 400 self._uninterruptible = True
403 401 yield
404 402 finally:
405 403 self._uninterruptible = False
406 404
407 405 def formatter(self, topic, opts):
408 406 return formatter.formatter(self, self, topic, opts)
409 407
410 408 def _trusted(self, fp, f):
411 409 st = util.fstat(fp)
412 410 if util.isowner(st):
413 411 return True
414 412
415 413 tusers, tgroups = self._trustusers, self._trustgroups
416 414 if b'*' in tusers or b'*' in tgroups:
417 415 return True
418 416
419 417 user = util.username(st.st_uid)
420 418 group = util.groupname(st.st_gid)
421 419 if user in tusers or group in tgroups or user == util.username():
422 420 return True
423 421
424 422 if self._reportuntrusted:
425 423 self.warn(
426 424 _(
427 425 b'not trusting file %s from untrusted '
428 426 b'user %s, group %s\n'
429 427 )
430 428 % (f, user, group)
431 429 )
432 430 return False
433 431
434 432 def read_resource_config(
435 433 self, name, root=None, trust=False, sections=None, remap=None
436 434 ):
437 435 try:
438 436 fp = resourceutil.open_resource(name[0], name[1])
439 437 except IOError:
440 438 if not sections: # ignore unless we were looking for something
441 439 return
442 440 raise
443 441
444 442 self._readconfig(
445 443 b'resource:%s.%s' % name, fp, root, trust, sections, remap
446 444 )
447 445
448 446 def readconfig(
449 447 self, filename, root=None, trust=False, sections=None, remap=None
450 448 ):
451 449 try:
452 450 fp = open(filename, 'rb')
453 451 except IOError:
454 452 if not sections: # ignore unless we were looking for something
455 453 return
456 454 raise
457 455
458 456 self._readconfig(filename, fp, root, trust, sections, remap)
459 457
460 458 def _readconfig(
461 459 self, filename, fp, root=None, trust=False, sections=None, remap=None
462 460 ):
463 461 with fp:
464 462 cfg = config.config()
465 463 trusted = sections or trust or self._trusted(fp, filename)
466 464
467 465 try:
468 466 cfg.read(filename, fp, sections=sections, remap=remap)
469 467 except error.ConfigError as inst:
470 468 if trusted:
471 469 raise
472 470 self.warn(
473 471 _(b'ignored %s: %s\n') % (inst.location, inst.message)
474 472 )
475 473
476 474 self._applyconfig(cfg, trusted, root)
477 475
478 476 def applyconfig(self, configitems, source=b"", root=None):
479 477 """Add configitems from a non-file source. Unlike with ``setconfig()``,
480 478 they can be overridden by subsequent config file reads. The items are
481 479 in the same format as ``configoverride()``, namely a dict of the
482 480 following structures: {(section, name) : value}
483 481
484 482 Typically this is used by extensions that inject themselves into the
485 483 config file load procedure by monkeypatching ``localrepo.loadhgrc()``.
486 484 """
487 485 cfg = config.config()
488 486
489 487 for (section, name), value in configitems.items():
490 488 cfg.set(section, name, value, source)
491 489
492 490 self._applyconfig(cfg, True, root)
493 491
494 492 def _applyconfig(self, cfg, trusted, root):
495 493 if self.plain():
496 494 for k in (
497 495 b'debug',
498 496 b'fallbackencoding',
499 497 b'quiet',
500 498 b'slash',
501 499 b'logtemplate',
502 500 b'message-output',
503 501 b'statuscopies',
504 502 b'style',
505 503 b'traceback',
506 504 b'verbose',
507 505 ):
508 506 if k in cfg[b'ui']:
509 507 del cfg[b'ui'][k]
510 508 for k, v in cfg.items(b'defaults'):
511 509 del cfg[b'defaults'][k]
512 510 for k, v in cfg.items(b'commands'):
513 511 del cfg[b'commands'][k]
514 512 for k, v in cfg.items(b'command-templates'):
515 513 del cfg[b'command-templates'][k]
516 514 # Don't remove aliases from the configuration if in the exceptionlist
517 515 if self.plain(b'alias'):
518 516 for k, v in cfg.items(b'alias'):
519 517 del cfg[b'alias'][k]
520 518 if self.plain(b'revsetalias'):
521 519 for k, v in cfg.items(b'revsetalias'):
522 520 del cfg[b'revsetalias'][k]
523 521 if self.plain(b'templatealias'):
524 522 for k, v in cfg.items(b'templatealias'):
525 523 del cfg[b'templatealias'][k]
526 524
527 525 if trusted:
528 526 self._tcfg.update(cfg)
529 527 self._tcfg.update(self._ocfg)
530 528 self._ucfg.update(cfg)
531 529 self._ucfg.update(self._ocfg)
532 530
533 531 if root is None:
534 532 root = os.path.expanduser(b'~')
535 533 self.fixconfig(root=root)
536 534
537 535 def fixconfig(self, root=None, section=None):
538 536 if section in (None, b'paths'):
539 537 # expand vars and ~
540 538 # translate paths relative to root (or home) into absolute paths
541 539 root = root or encoding.getcwd()
542 540 for c in self._tcfg, self._ucfg, self._ocfg:
543 541 for n, p in c.items(b'paths'):
544 542 # Ignore sub-options.
545 543 if b':' in n:
546 544 continue
547 545 if not p:
548 546 continue
549 547 if b'%%' in p:
550 548 s = self.configsource(b'paths', n) or b'none'
551 549 self.warn(
552 550 _(b"(deprecated '%%' in path %s=%s from %s)\n")
553 551 % (n, p, s)
554 552 )
555 553 p = p.replace(b'%%', b'%')
556 554 p = util.expandpath(p)
557 555 if not util.hasscheme(p) and not os.path.isabs(p):
558 556 p = os.path.normpath(os.path.join(root, p))
559 557 c.set(b"paths", n, p)
560 558
561 559 if section in (None, b'ui'):
562 560 # update ui options
563 561 self._fmsgout, self._fmsgerr = _selectmsgdests(self)
564 562 self.debugflag = self.configbool(b'ui', b'debug')
565 563 self.verbose = self.debugflag or self.configbool(b'ui', b'verbose')
566 564 self.quiet = not self.debugflag and self.configbool(b'ui', b'quiet')
567 565 if self.verbose and self.quiet:
568 566 self.quiet = self.verbose = False
569 567 self._reportuntrusted = self.debugflag or self.configbool(
570 568 b"ui", b"report_untrusted"
571 569 )
572 570 self.showtimestamp = self.configbool(b'ui', b'timestamp-output')
573 571 self.tracebackflag = self.configbool(b'ui', b'traceback')
574 572 self.logblockedtimes = self.configbool(b'ui', b'logblockedtimes')
575 573
576 574 if section in (None, b'trusted'):
577 575 # update trust information
578 576 self._trustusers.update(self.configlist(b'trusted', b'users'))
579 577 self._trustgroups.update(self.configlist(b'trusted', b'groups'))
580 578
581 579 if section in (None, b'devel', b'ui') and self.debugflag:
582 580 tracked = set()
583 581 if self.configbool(b'devel', b'debug.extensions'):
584 582 tracked.add(b'extension')
585 583 if tracked:
586 584 logger = loggingutil.fileobjectlogger(self._ferr, tracked)
587 585 self.setlogger(b'debug', logger)
588 586
589 587 def backupconfig(self, section, item):
590 588 return (
591 589 self._ocfg.backup(section, item),
592 590 self._tcfg.backup(section, item),
593 591 self._ucfg.backup(section, item),
594 592 )
595 593
596 594 def restoreconfig(self, data):
597 595 self._ocfg.restore(data[0])
598 596 self._tcfg.restore(data[1])
599 597 self._ucfg.restore(data[2])
600 598
601 599 def setconfig(self, section, name, value, source=b''):
602 600 for cfg in (self._ocfg, self._tcfg, self._ucfg):
603 601 cfg.set(section, name, value, source)
604 602 self.fixconfig(section=section)
605 603 self._maybetweakdefaults()
606 604
607 605 def _data(self, untrusted):
608 606 return untrusted and self._ucfg or self._tcfg
609 607
610 608 def configsource(self, section, name, untrusted=False):
611 609 return self._data(untrusted).source(section, name)
612 610
613 611 def config(self, section, name, default=_unset, untrusted=False):
614 612 """return the plain string version of a config"""
615 613 value = self._config(
616 614 section, name, default=default, untrusted=untrusted
617 615 )
618 616 if value is _unset:
619 617 return None
620 618 return value
621 619
622 620 def _config(self, section, name, default=_unset, untrusted=False):
623 621 value = itemdefault = default
624 622 item = self._knownconfig.get(section, {}).get(name)
625 623 alternates = [(section, name)]
626 624
627 625 if item is not None:
628 626 alternates.extend(item.alias)
629 627 if callable(item.default):
630 628 itemdefault = item.default()
631 629 else:
632 630 itemdefault = item.default
633 631 else:
634 632 msg = b"accessing unregistered config item: '%s.%s'"
635 633 msg %= (section, name)
636 634 self.develwarn(msg, 2, b'warn-config-unknown')
637 635
638 636 if default is _unset:
639 637 if item is None:
640 638 value = default
641 639 elif item.default is configitems.dynamicdefault:
642 640 value = None
643 641 msg = b"config item requires an explicit default value: '%s.%s'"
644 642 msg %= (section, name)
645 643 self.develwarn(msg, 2, b'warn-config-default')
646 644 else:
647 645 value = itemdefault
648 646 elif (
649 647 item is not None
650 648 and item.default is not configitems.dynamicdefault
651 649 and default != itemdefault
652 650 ):
653 651 msg = (
654 652 b"specifying a mismatched default value for a registered "
655 653 b"config item: '%s.%s' '%s'"
656 654 )
657 655 msg %= (section, name, pycompat.bytestr(default))
658 656 self.develwarn(msg, 2, b'warn-config-default')
659 657
660 658 for s, n in alternates:
661 659 candidate = self._data(untrusted).get(s, n, None)
662 660 if candidate is not None:
663 661 value = candidate
664 662 break
665 663
666 664 if self.debugflag and not untrusted and self._reportuntrusted:
667 665 for s, n in alternates:
668 666 uvalue = self._ucfg.get(s, n)
669 667 if uvalue is not None and uvalue != value:
670 668 self.debug(
671 669 b"ignoring untrusted configuration option "
672 670 b"%s.%s = %s\n" % (s, n, uvalue)
673 671 )
674 672 return value
675 673
676 674 def config_default(self, section, name):
677 675 """return the default value for a config option
678 676
679 677 The default is returned "raw", for example if it is a callable, the
680 678 callable was not called.
681 679 """
682 680 item = self._knownconfig.get(section, {}).get(name)
683 681
684 682 if item is None:
685 683 raise KeyError((section, name))
686 684 return item.default
687 685
688 686 def configsuboptions(self, section, name, default=_unset, untrusted=False):
689 687 """Get a config option and all sub-options.
690 688
691 689 Some config options have sub-options that are declared with the
692 690 format "key:opt = value". This method is used to return the main
693 691 option and all its declared sub-options.
694 692
695 693 Returns a 2-tuple of ``(option, sub-options)``, where `sub-options``
696 694 is a dict of defined sub-options where keys and values are strings.
697 695 """
698 696 main = self.config(section, name, default, untrusted=untrusted)
699 697 data = self._data(untrusted)
700 698 sub = {}
701 699 prefix = b'%s:' % name
702 700 for k, v in data.items(section):
703 701 if k.startswith(prefix):
704 702 sub[k[len(prefix) :]] = v
705 703
706 704 if self.debugflag and not untrusted and self._reportuntrusted:
707 705 for k, v in sub.items():
708 706 uvalue = self._ucfg.get(section, b'%s:%s' % (name, k))
709 707 if uvalue is not None and uvalue != v:
710 708 self.debug(
711 709 b'ignoring untrusted configuration option '
712 710 b'%s:%s.%s = %s\n' % (section, name, k, uvalue)
713 711 )
714 712
715 713 return main, sub
716 714
717 715 def configpath(self, section, name, default=_unset, untrusted=False):
718 716 """get a path config item, expanded relative to repo root or config
719 717 file"""
720 718 v = self.config(section, name, default, untrusted)
721 719 if v is None:
722 720 return None
723 721 if not os.path.isabs(v) or b"://" not in v:
724 722 src = self.configsource(section, name, untrusted)
725 723 if b':' in src:
726 724 base = os.path.dirname(src.rsplit(b':')[0])
727 725 v = os.path.join(base, os.path.expanduser(v))
728 726 return v
729 727
730 728 def configbool(self, section, name, default=_unset, untrusted=False):
731 729 """parse a configuration element as a boolean
732 730
733 731 >>> u = ui(); s = b'foo'
734 732 >>> u.setconfig(s, b'true', b'yes')
735 733 >>> u.configbool(s, b'true')
736 734 True
737 735 >>> u.setconfig(s, b'false', b'no')
738 736 >>> u.configbool(s, b'false')
739 737 False
740 738 >>> u.configbool(s, b'unknown')
741 739 False
742 740 >>> u.configbool(s, b'unknown', True)
743 741 True
744 742 >>> u.setconfig(s, b'invalid', b'somevalue')
745 743 >>> u.configbool(s, b'invalid')
746 744 Traceback (most recent call last):
747 745 ...
748 746 ConfigError: foo.invalid is not a boolean ('somevalue')
749 747 """
750 748
751 749 v = self._config(section, name, default, untrusted=untrusted)
752 750 if v is None:
753 751 return v
754 752 if v is _unset:
755 753 if default is _unset:
756 754 return False
757 755 return default
758 756 if isinstance(v, bool):
759 757 return v
760 758 b = stringutil.parsebool(v)
761 759 if b is None:
762 760 raise error.ConfigError(
763 761 _(b"%s.%s is not a boolean ('%s')") % (section, name, v)
764 762 )
765 763 return b
766 764
767 765 def configwith(
768 766 self, convert, section, name, default=_unset, desc=None, untrusted=False
769 767 ):
770 768 """parse a configuration element with a conversion function
771 769
772 770 >>> u = ui(); s = b'foo'
773 771 >>> u.setconfig(s, b'float1', b'42')
774 772 >>> u.configwith(float, s, b'float1')
775 773 42.0
776 774 >>> u.setconfig(s, b'float2', b'-4.25')
777 775 >>> u.configwith(float, s, b'float2')
778 776 -4.25
779 777 >>> u.configwith(float, s, b'unknown', 7)
780 778 7.0
781 779 >>> u.setconfig(s, b'invalid', b'somevalue')
782 780 >>> u.configwith(float, s, b'invalid')
783 781 Traceback (most recent call last):
784 782 ...
785 783 ConfigError: foo.invalid is not a valid float ('somevalue')
786 784 >>> u.configwith(float, s, b'invalid', desc=b'womble')
787 785 Traceback (most recent call last):
788 786 ...
789 787 ConfigError: foo.invalid is not a valid womble ('somevalue')
790 788 """
791 789
792 790 v = self.config(section, name, default, untrusted)
793 791 if v is None:
794 792 return v # do not attempt to convert None
795 793 try:
796 794 return convert(v)
797 795 except (ValueError, error.ParseError):
798 796 if desc is None:
799 797 desc = pycompat.sysbytes(convert.__name__)
800 798 raise error.ConfigError(
801 799 _(b"%s.%s is not a valid %s ('%s')") % (section, name, desc, v)
802 800 )
803 801
804 802 def configint(self, section, name, default=_unset, untrusted=False):
805 803 """parse a configuration element as an integer
806 804
807 805 >>> u = ui(); s = b'foo'
808 806 >>> u.setconfig(s, b'int1', b'42')
809 807 >>> u.configint(s, b'int1')
810 808 42
811 809 >>> u.setconfig(s, b'int2', b'-42')
812 810 >>> u.configint(s, b'int2')
813 811 -42
814 812 >>> u.configint(s, b'unknown', 7)
815 813 7
816 814 >>> u.setconfig(s, b'invalid', b'somevalue')
817 815 >>> u.configint(s, b'invalid')
818 816 Traceback (most recent call last):
819 817 ...
820 818 ConfigError: foo.invalid is not a valid integer ('somevalue')
821 819 """
822 820
823 821 return self.configwith(
824 822 int, section, name, default, b'integer', untrusted
825 823 )
826 824
827 825 def configbytes(self, section, name, default=_unset, untrusted=False):
828 826 """parse a configuration element as a quantity in bytes
829 827
830 828 Units can be specified as b (bytes), k or kb (kilobytes), m or
831 829 mb (megabytes), g or gb (gigabytes).
832 830
833 831 >>> u = ui(); s = b'foo'
834 832 >>> u.setconfig(s, b'val1', b'42')
835 833 >>> u.configbytes(s, b'val1')
836 834 42
837 835 >>> u.setconfig(s, b'val2', b'42.5 kb')
838 836 >>> u.configbytes(s, b'val2')
839 837 43520
840 838 >>> u.configbytes(s, b'unknown', b'7 MB')
841 839 7340032
842 840 >>> u.setconfig(s, b'invalid', b'somevalue')
843 841 >>> u.configbytes(s, b'invalid')
844 842 Traceback (most recent call last):
845 843 ...
846 844 ConfigError: foo.invalid is not a byte quantity ('somevalue')
847 845 """
848 846
849 847 value = self._config(section, name, default, untrusted)
850 848 if value is _unset:
851 849 if default is _unset:
852 850 default = 0
853 851 value = default
854 852 if not isinstance(value, bytes):
855 853 return value
856 854 try:
857 855 return util.sizetoint(value)
858 856 except error.ParseError:
859 857 raise error.ConfigError(
860 858 _(b"%s.%s is not a byte quantity ('%s')")
861 859 % (section, name, value)
862 860 )
863 861
864 862 def configlist(self, section, name, default=_unset, untrusted=False):
865 863 """parse a configuration element as a list of comma/space separated
866 864 strings
867 865
868 866 >>> u = ui(); s = b'foo'
869 867 >>> u.setconfig(s, b'list1', b'this,is "a small" ,test')
870 868 >>> u.configlist(s, b'list1')
871 869 ['this', 'is', 'a small', 'test']
872 870 >>> u.setconfig(s, b'list2', b'this, is "a small" , test ')
873 871 >>> u.configlist(s, b'list2')
874 872 ['this', 'is', 'a small', 'test']
875 873 """
876 874 # default is not always a list
877 875 v = self.configwith(
878 876 config.parselist, section, name, default, b'list', untrusted
879 877 )
880 878 if isinstance(v, bytes):
881 879 return config.parselist(v)
882 880 elif v is None:
883 881 return []
884 882 return v
885 883
886 884 def configdate(self, section, name, default=_unset, untrusted=False):
887 885 """parse a configuration element as a tuple of ints
888 886
889 887 >>> u = ui(); s = b'foo'
890 888 >>> u.setconfig(s, b'date', b'0 0')
891 889 >>> u.configdate(s, b'date')
892 890 (0, 0)
893 891 """
894 892 if self.config(section, name, default, untrusted):
895 893 return self.configwith(
896 894 dateutil.parsedate, section, name, default, b'date', untrusted
897 895 )
898 896 if default is _unset:
899 897 return None
900 898 return default
901 899
902 900 def configdefault(self, section, name):
903 901 """returns the default value of the config item"""
904 902 item = self._knownconfig.get(section, {}).get(name)
905 903 itemdefault = None
906 904 if item is not None:
907 905 if callable(item.default):
908 906 itemdefault = item.default()
909 907 else:
910 908 itemdefault = item.default
911 909 return itemdefault
912 910
913 911 def hasconfig(self, section, name, untrusted=False):
914 912 return self._data(untrusted).hasitem(section, name)
915 913
916 914 def has_section(self, section, untrusted=False):
917 915 '''tell whether section exists in config.'''
918 916 return section in self._data(untrusted)
919 917
920 918 def configitems(self, section, untrusted=False, ignoresub=False):
921 919 items = self._data(untrusted).items(section)
922 920 if ignoresub:
923 921 items = [i for i in items if b':' not in i[0]]
924 922 if self.debugflag and not untrusted and self._reportuntrusted:
925 923 for k, v in self._ucfg.items(section):
926 924 if self._tcfg.get(section, k) != v:
927 925 self.debug(
928 926 b"ignoring untrusted configuration option "
929 927 b"%s.%s = %s\n" % (section, k, v)
930 928 )
931 929 return items
932 930
933 931 def walkconfig(self, untrusted=False):
934 932 cfg = self._data(untrusted)
935 933 for section in cfg.sections():
936 934 for name, value in self.configitems(section, untrusted):
937 935 yield section, name, value
938 936
939 937 def plain(self, feature=None):
940 938 """is plain mode active?
941 939
942 940 Plain mode means that all configuration variables which affect
943 941 the behavior and output of Mercurial should be
944 942 ignored. Additionally, the output should be stable,
945 943 reproducible and suitable for use in scripts or applications.
946 944
947 945 The only way to trigger plain mode is by setting either the
948 946 `HGPLAIN' or `HGPLAINEXCEPT' environment variables.
949 947
950 948 The return value can either be
951 949 - False if HGPLAIN is not set, or feature is in HGPLAINEXCEPT
952 950 - False if feature is disabled by default and not included in HGPLAIN
953 951 - True otherwise
954 952 """
955 953 if (
956 954 b'HGPLAIN' not in encoding.environ
957 955 and b'HGPLAINEXCEPT' not in encoding.environ
958 956 ):
959 957 return False
960 958 exceptions = (
961 959 encoding.environ.get(b'HGPLAINEXCEPT', b'').strip().split(b',')
962 960 )
963 961 # TODO: add support for HGPLAIN=+feature,-feature syntax
964 962 if b'+strictflags' not in encoding.environ.get(b'HGPLAIN', b'').split(
965 963 b','
966 964 ):
967 965 exceptions.append(b'strictflags')
968 966 if feature and exceptions:
969 967 return feature not in exceptions
970 968 return True
971 969
972 970 def username(self, acceptempty=False):
973 971 """Return default username to be used in commits.
974 972
975 973 Searched in this order: $HGUSER, [ui] section of hgrcs, $EMAIL
976 974 and stop searching if one of these is set.
977 975 If not found and acceptempty is True, returns None.
978 976 If not found and ui.askusername is True, ask the user, else use
979 977 ($LOGNAME or $USER or $LNAME or $USERNAME) + "@full.hostname".
980 978 If no username could be found, raise an Abort error.
981 979 """
982 980 user = encoding.environ.get(b"HGUSER")
983 981 if user is None:
984 982 user = self.config(b"ui", b"username")
985 983 if user is not None:
986 984 user = os.path.expandvars(user)
987 985 if user is None:
988 986 user = encoding.environ.get(b"EMAIL")
989 987 if user is None and acceptempty:
990 988 return user
991 989 if user is None and self.configbool(b"ui", b"askusername"):
992 990 user = self.prompt(_(b"enter a commit username:"), default=None)
993 991 if user is None and not self.interactive():
994 992 try:
995 993 user = b'%s@%s' % (
996 994 procutil.getuser(),
997 995 encoding.strtolocal(socket.getfqdn()),
998 996 )
999 997 self.warn(_(b"no username found, using '%s' instead\n") % user)
1000 998 except KeyError:
1001 999 pass
1002 1000 if not user:
1003 1001 raise error.Abort(
1004 1002 _(b'no username supplied'),
1005 1003 hint=_(b"use 'hg config --edit' " b'to set your username'),
1006 1004 )
1007 1005 if b"\n" in user:
1008 1006 raise error.Abort(
1009 1007 _(b"username %r contains a newline\n") % pycompat.bytestr(user)
1010 1008 )
1011 1009 return user
1012 1010
1013 1011 def shortuser(self, user):
1014 1012 """Return a short representation of a user name or email address."""
1015 1013 if not self.verbose:
1016 1014 user = stringutil.shortuser(user)
1017 1015 return user
1018 1016
1019 1017 def expandpath(self, loc, default=None):
1020 1018 """Return repository location relative to cwd or from [paths]"""
1021 1019 try:
1022 1020 p = self.paths.getpath(loc)
1023 1021 if p:
1024 1022 return p.rawloc
1025 1023 except error.RepoError:
1026 1024 pass
1027 1025
1028 1026 if default:
1029 1027 try:
1030 1028 p = self.paths.getpath(default)
1031 1029 if p:
1032 1030 return p.rawloc
1033 1031 except error.RepoError:
1034 1032 pass
1035 1033
1036 1034 return loc
1037 1035
1038 1036 @util.propertycache
1039 1037 def paths(self):
1040 1038 return paths(self)
1041 1039
1042 1040 @property
1043 1041 def fout(self):
1044 1042 return self._fout
1045 1043
1046 1044 @fout.setter
1047 1045 def fout(self, f):
1048 1046 self._fout = f
1049 1047 self._fmsgout, self._fmsgerr = _selectmsgdests(self)
1050 1048
1051 1049 @property
1052 1050 def ferr(self):
1053 1051 return self._ferr
1054 1052
1055 1053 @ferr.setter
1056 1054 def ferr(self, f):
1057 1055 self._ferr = f
1058 1056 self._fmsgout, self._fmsgerr = _selectmsgdests(self)
1059 1057
1060 1058 @property
1061 1059 def fin(self):
1062 1060 return self._fin
1063 1061
1064 1062 @fin.setter
1065 1063 def fin(self, f):
1066 1064 self._fin = f
1067 1065
1068 1066 @property
1069 1067 def fmsg(self):
1070 1068 """Stream dedicated for status/error messages; may be None if
1071 1069 fout/ferr are used"""
1072 1070 return self._fmsg
1073 1071
1074 1072 @fmsg.setter
1075 1073 def fmsg(self, f):
1076 1074 self._fmsg = f
1077 1075 self._fmsgout, self._fmsgerr = _selectmsgdests(self)
1078 1076
1079 1077 def pushbuffer(self, error=False, subproc=False, labeled=False):
1080 1078 """install a buffer to capture standard output of the ui object
1081 1079
1082 1080 If error is True, the error output will be captured too.
1083 1081
1084 1082 If subproc is True, output from subprocesses (typically hooks) will be
1085 1083 captured too.
1086 1084
1087 1085 If labeled is True, any labels associated with buffered
1088 1086 output will be handled. By default, this has no effect
1089 1087 on the output returned, but extensions and GUI tools may
1090 1088 handle this argument and returned styled output. If output
1091 1089 is being buffered so it can be captured and parsed or
1092 1090 processed, labeled should not be set to True.
1093 1091 """
1094 1092 self._buffers.append([])
1095 1093 self._bufferstates.append((error, subproc, labeled))
1096 1094 self._bufferapplylabels = labeled
1097 1095
1098 1096 def popbuffer(self):
1099 1097 '''pop the last buffer and return the buffered output'''
1100 1098 self._bufferstates.pop()
1101 1099 if self._bufferstates:
1102 1100 self._bufferapplylabels = self._bufferstates[-1][2]
1103 1101 else:
1104 1102 self._bufferapplylabels = None
1105 1103
1106 1104 return b"".join(self._buffers.pop())
1107 1105
1108 1106 def _isbuffered(self, dest):
1109 1107 if dest is self._fout:
1110 1108 return bool(self._buffers)
1111 1109 if dest is self._ferr:
1112 1110 return bool(self._bufferstates and self._bufferstates[-1][0])
1113 1111 return False
1114 1112
1115 1113 def canwritewithoutlabels(self):
1116 1114 '''check if write skips the label'''
1117 1115 if self._buffers and not self._bufferapplylabels:
1118 1116 return True
1119 1117 return self._colormode is None
1120 1118
1121 1119 def canbatchlabeledwrites(self):
1122 1120 '''check if write calls with labels are batchable'''
1123 1121 # Windows color printing is special, see ``write``.
1124 1122 return self._colormode != b'win32'
1125 1123
1126 1124 def write(self, *args, **opts):
1127 1125 """write args to output
1128 1126
1129 1127 By default, this method simply writes to the buffer or stdout.
1130 1128 Color mode can be set on the UI class to have the output decorated
1131 1129 with color modifier before being written to stdout.
1132 1130
1133 1131 The color used is controlled by an optional keyword argument, "label".
1134 1132 This should be a string containing label names separated by space.
1135 1133 Label names take the form of "topic.type". For example, ui.debug()
1136 1134 issues a label of "ui.debug".
1137 1135
1138 1136 Progress reports via stderr are normally cleared before writing as
1139 1137 stdout and stderr go to the same terminal. This can be skipped with
1140 1138 the optional keyword argument "keepprogressbar". The progress bar
1141 1139 will continue to occupy a partial line on stderr in that case.
1142 1140 This functionality is intended when Mercurial acts as data source
1143 1141 in a pipe.
1144 1142
1145 1143 When labeling output for a specific command, a label of
1146 1144 "cmdname.type" is recommended. For example, status issues
1147 1145 a label of "status.modified" for modified files.
1148 1146 """
1149 1147 dest = self._fout
1150 1148
1151 1149 # inlined _write() for speed
1152 1150 if self._buffers:
1153 1151 label = opts.get('label', b'')
1154 1152 if label and self._bufferapplylabels:
1155 1153 self._buffers[-1].extend(self.label(a, label) for a in args)
1156 1154 else:
1157 1155 self._buffers[-1].extend(args)
1158 1156 return
1159 1157
1160 1158 # inlined _writenobuf() for speed
1161 1159 if not opts.get('keepprogressbar', False):
1162 1160 self._progclear()
1163 1161 msg = b''.join(args)
1164 1162
1165 1163 # opencode timeblockedsection because this is a critical path
1166 1164 starttime = util.timer()
1167 1165 try:
1168 1166 if self._colormode == b'win32':
1169 1167 # windows color printing is its own can of crab, defer to
1170 1168 # the color module and that is it.
1171 1169 color.win32print(self, dest.write, msg, **opts)
1172 1170 else:
1173 1171 if self._colormode is not None:
1174 1172 label = opts.get('label', b'')
1175 1173 msg = self.label(msg, label)
1176 1174 dest.write(msg)
1177 1175 except IOError as err:
1178 1176 raise error.StdioError(err)
1179 1177 finally:
1180 1178 self._blockedtimes[b'stdio_blocked'] += (
1181 1179 util.timer() - starttime
1182 1180 ) * 1000
1183 1181
1184 1182 def write_err(self, *args, **opts):
1185 1183 self._write(self._ferr, *args, **opts)
1186 1184
1187 1185 def _write(self, dest, *args, **opts):
1188 1186 # update write() as well if you touch this code
1189 1187 if self._isbuffered(dest):
1190 1188 label = opts.get('label', b'')
1191 1189 if label and self._bufferapplylabels:
1192 1190 self._buffers[-1].extend(self.label(a, label) for a in args)
1193 1191 else:
1194 1192 self._buffers[-1].extend(args)
1195 1193 else:
1196 1194 self._writenobuf(dest, *args, **opts)
1197 1195
1198 1196 def _writenobuf(self, dest, *args, **opts):
1199 1197 # update write() as well if you touch this code
1200 1198 if not opts.get('keepprogressbar', False):
1201 1199 self._progclear()
1202 1200 msg = b''.join(args)
1203 1201
1204 1202 # opencode timeblockedsection because this is a critical path
1205 1203 starttime = util.timer()
1206 1204 try:
1207 1205 if dest is self._ferr and not getattr(self._fout, 'closed', False):
1208 1206 self._fout.flush()
1209 1207 if getattr(dest, 'structured', False):
1210 1208 # channel for machine-readable output with metadata, where
1211 1209 # no extra colorization is necessary.
1212 1210 dest.write(msg, **opts)
1213 1211 elif self._colormode == b'win32':
1214 1212 # windows color printing is its own can of crab, defer to
1215 1213 # the color module and that is it.
1216 1214 color.win32print(self, dest.write, msg, **opts)
1217 1215 else:
1218 1216 if self._colormode is not None:
1219 1217 label = opts.get('label', b'')
1220 1218 msg = self.label(msg, label)
1221 1219 dest.write(msg)
1222 1220 # stderr may be buffered under win32 when redirected to files,
1223 1221 # including stdout.
1224 1222 if dest is self._ferr and not getattr(dest, 'closed', False):
1225 1223 dest.flush()
1226 1224 except IOError as err:
1227 1225 if dest is self._ferr and err.errno in (
1228 1226 errno.EPIPE,
1229 1227 errno.EIO,
1230 1228 errno.EBADF,
1231 1229 ):
1232 1230 # no way to report the error, so ignore it
1233 1231 return
1234 1232 raise error.StdioError(err)
1235 1233 finally:
1236 1234 self._blockedtimes[b'stdio_blocked'] += (
1237 1235 util.timer() - starttime
1238 1236 ) * 1000
1239 1237
1240 1238 def _writemsg(self, dest, *args, **opts):
1241 1239 timestamp = self.showtimestamp and opts.get('type') in {
1242 1240 b'debug',
1243 1241 b'error',
1244 1242 b'note',
1245 1243 b'status',
1246 1244 b'warning',
1247 1245 }
1248 1246 if timestamp:
1249 1247 args = (
1250 1248 b'[%s] '
1251 1249 % pycompat.bytestr(datetime.datetime.now().isoformat()),
1252 1250 ) + args
1253 1251 _writemsgwith(self._write, dest, *args, **opts)
1254 1252 if timestamp:
1255 1253 dest.flush()
1256 1254
1257 1255 def _writemsgnobuf(self, dest, *args, **opts):
1258 1256 _writemsgwith(self._writenobuf, dest, *args, **opts)
1259 1257
1260 1258 def flush(self):
1261 1259 # opencode timeblockedsection because this is a critical path
1262 1260 starttime = util.timer()
1263 1261 try:
1264 1262 try:
1265 1263 self._fout.flush()
1266 1264 except IOError as err:
1267 1265 if err.errno not in (errno.EPIPE, errno.EIO, errno.EBADF):
1268 1266 raise error.StdioError(err)
1269 1267 finally:
1270 1268 try:
1271 1269 self._ferr.flush()
1272 1270 except IOError as err:
1273 1271 if err.errno not in (errno.EPIPE, errno.EIO, errno.EBADF):
1274 1272 raise error.StdioError(err)
1275 1273 finally:
1276 1274 self._blockedtimes[b'stdio_blocked'] += (
1277 1275 util.timer() - starttime
1278 1276 ) * 1000
1279 1277
1280 1278 def _isatty(self, fh):
1281 1279 if self.configbool(b'ui', b'nontty'):
1282 1280 return False
1283 1281 return procutil.isatty(fh)
1284 1282
1285 1283 def protectfinout(self):
1286 1284 """Duplicate ui streams and redirect original if they are stdio
1287 1285
1288 1286 Returns (fin, fout) which point to the original ui fds, but may be
1289 1287 copy of them. The returned streams can be considered "owned" in that
1290 1288 print(), exec(), etc. never reach to them.
1291 1289 """
1292 1290 if self._finoutredirected:
1293 1291 # if already redirected, protectstdio() would just create another
1294 1292 # nullfd pair, which is equivalent to returning self._fin/_fout.
1295 1293 return self._fin, self._fout
1296 1294 fin, fout = procutil.protectstdio(self._fin, self._fout)
1297 1295 self._finoutredirected = (fin, fout) != (self._fin, self._fout)
1298 1296 return fin, fout
1299 1297
1300 1298 def restorefinout(self, fin, fout):
1301 1299 """Restore ui streams from possibly duplicated (fin, fout)"""
1302 1300 if (fin, fout) == (self._fin, self._fout):
1303 1301 return
1304 1302 procutil.restorestdio(self._fin, self._fout, fin, fout)
1305 1303 # protectfinout() won't create more than one duplicated streams,
1306 1304 # so we can just turn the redirection flag off.
1307 1305 self._finoutredirected = False
1308 1306
1309 1307 @contextlib.contextmanager
1310 1308 def protectedfinout(self):
1311 1309 """Run code block with protected standard streams"""
1312 1310 fin, fout = self.protectfinout()
1313 1311 try:
1314 1312 yield fin, fout
1315 1313 finally:
1316 1314 self.restorefinout(fin, fout)
1317 1315
1318 1316 def disablepager(self):
1319 1317 self._disablepager = True
1320 1318
1321 1319 def pager(self, command):
1322 1320 """Start a pager for subsequent command output.
1323 1321
1324 1322 Commands which produce a long stream of output should call
1325 1323 this function to activate the user's preferred pagination
1326 1324 mechanism (which may be no pager). Calling this function
1327 1325 precludes any future use of interactive functionality, such as
1328 1326 prompting the user or activating curses.
1329 1327
1330 1328 Args:
1331 1329 command: The full, non-aliased name of the command. That is, "log"
1332 1330 not "history, "summary" not "summ", etc.
1333 1331 """
1334 1332 if self._disablepager or self.pageractive:
1335 1333 # how pager should do is already determined
1336 1334 return
1337 1335
1338 1336 if not command.startswith(b'internal-always-') and (
1339 1337 # explicit --pager=on (= 'internal-always-' prefix) should
1340 1338 # take precedence over disabling factors below
1341 1339 command in self.configlist(b'pager', b'ignore')
1342 1340 or not self.configbool(b'ui', b'paginate')
1343 1341 or not self.configbool(b'pager', b'attend-' + command, True)
1344 1342 or encoding.environ.get(b'TERM') == b'dumb'
1345 1343 # TODO: if we want to allow HGPLAINEXCEPT=pager,
1346 1344 # formatted() will need some adjustment.
1347 1345 or not self.formatted()
1348 1346 or self.plain()
1349 1347 or self._buffers
1350 1348 # TODO: expose debugger-enabled on the UI object
1351 1349 or b'--debugger' in pycompat.sysargv
1352 1350 ):
1353 1351 # We only want to paginate if the ui appears to be
1354 1352 # interactive, the user didn't say HGPLAIN or
1355 1353 # HGPLAINEXCEPT=pager, and the user didn't specify --debug.
1356 1354 return
1357 1355
1358 1356 pagercmd = self.config(b'pager', b'pager', rcutil.fallbackpager)
1359 1357 if not pagercmd:
1360 1358 return
1361 1359
1362 1360 pagerenv = {}
1363 1361 for name, value in rcutil.defaultpagerenv().items():
1364 1362 if name not in encoding.environ:
1365 1363 pagerenv[name] = value
1366 1364
1367 1365 self.debug(
1368 1366 b'starting pager for command %s\n' % stringutil.pprint(command)
1369 1367 )
1370 1368 self.flush()
1371 1369
1372 1370 wasformatted = self.formatted()
1373 1371 if util.safehasattr(signal, b"SIGPIPE"):
1374 1372 signal.signal(signal.SIGPIPE, _catchterm)
1375 1373 if self._runpager(pagercmd, pagerenv):
1376 1374 self.pageractive = True
1377 1375 # Preserve the formatted-ness of the UI. This is important
1378 1376 # because we mess with stdout, which might confuse
1379 1377 # auto-detection of things being formatted.
1380 1378 self.setconfig(b'ui', b'formatted', wasformatted, b'pager')
1381 1379 self.setconfig(b'ui', b'interactive', False, b'pager')
1382 1380
1383 1381 # If pagermode differs from color.mode, reconfigure color now that
1384 1382 # pageractive is set.
1385 1383 cm = self._colormode
1386 1384 if cm != self.config(b'color', b'pagermode', cm):
1387 1385 color.setup(self)
1388 1386 else:
1389 1387 # If the pager can't be spawned in dispatch when --pager=on is
1390 1388 # given, don't try again when the command runs, to avoid a duplicate
1391 1389 # warning about a missing pager command.
1392 1390 self.disablepager()
1393 1391
1394 1392 def _runpager(self, command, env=None):
1395 1393 """Actually start the pager and set up file descriptors.
1396 1394
1397 1395 This is separate in part so that extensions (like chg) can
1398 1396 override how a pager is invoked.
1399 1397 """
1400 1398 if command == b'cat':
1401 1399 # Save ourselves some work.
1402 1400 return False
1403 1401 # If the command doesn't contain any of these characters, we
1404 1402 # assume it's a binary and exec it directly. This means for
1405 1403 # simple pager command configurations, we can degrade
1406 1404 # gracefully and tell the user about their broken pager.
1407 1405 shell = any(c in command for c in b"|&;<>()$`\\\"' \t\n*?[#~=%")
1408 1406
1409 1407 if pycompat.iswindows and not shell:
1410 1408 # Window's built-in `more` cannot be invoked with shell=False, but
1411 1409 # its `more.com` can. Hide this implementation detail from the
1412 1410 # user so we can also get sane bad PAGER behavior. MSYS has
1413 1411 # `more.exe`, so do a cmd.exe style resolution of the executable to
1414 1412 # determine which one to use.
1415 1413 fullcmd = procutil.findexe(command)
1416 1414 if not fullcmd:
1417 1415 self.warn(
1418 1416 _(b"missing pager command '%s', skipping pager\n") % command
1419 1417 )
1420 1418 return False
1421 1419
1422 1420 command = fullcmd
1423 1421
1424 1422 try:
1425 1423 pager = subprocess.Popen(
1426 1424 procutil.tonativestr(command),
1427 1425 shell=shell,
1428 1426 bufsize=-1,
1429 1427 close_fds=procutil.closefds,
1430 1428 stdin=subprocess.PIPE,
1431 1429 stdout=procutil.stdout,
1432 1430 stderr=procutil.stderr,
1433 1431 env=procutil.tonativeenv(procutil.shellenviron(env)),
1434 1432 )
1435 1433 except OSError as e:
1436 1434 if e.errno == errno.ENOENT and not shell:
1437 1435 self.warn(
1438 1436 _(b"missing pager command '%s', skipping pager\n") % command
1439 1437 )
1440 1438 return False
1441 1439 raise
1442 1440
1443 1441 # back up original file descriptors
1444 1442 stdoutfd = os.dup(procutil.stdout.fileno())
1445 1443 stderrfd = os.dup(procutil.stderr.fileno())
1446 1444
1447 1445 os.dup2(pager.stdin.fileno(), procutil.stdout.fileno())
1448 1446 if self._isatty(procutil.stderr):
1449 1447 os.dup2(pager.stdin.fileno(), procutil.stderr.fileno())
1450 1448
1451 1449 @self.atexit
1452 1450 def killpager():
1453 1451 if util.safehasattr(signal, b"SIGINT"):
1454 1452 signal.signal(signal.SIGINT, signal.SIG_IGN)
1455 1453 # restore original fds, closing pager.stdin copies in the process
1456 1454 os.dup2(stdoutfd, procutil.stdout.fileno())
1457 1455 os.dup2(stderrfd, procutil.stderr.fileno())
1458 1456 pager.stdin.close()
1459 1457 pager.wait()
1460 1458
1461 1459 return True
1462 1460
1463 1461 @property
1464 1462 def _exithandlers(self):
1465 1463 return _reqexithandlers
1466 1464
1467 1465 def atexit(self, func, *args, **kwargs):
1468 1466 """register a function to run after dispatching a request
1469 1467
1470 1468 Handlers do not stay registered across request boundaries."""
1471 1469 self._exithandlers.append((func, args, kwargs))
1472 1470 return func
1473 1471
1474 1472 def interface(self, feature):
1475 1473 """what interface to use for interactive console features?
1476 1474
1477 1475 The interface is controlled by the value of `ui.interface` but also by
1478 1476 the value of feature-specific configuration. For example:
1479 1477
1480 1478 ui.interface.histedit = text
1481 1479 ui.interface.chunkselector = curses
1482 1480
1483 1481 Here the features are "histedit" and "chunkselector".
1484 1482
1485 1483 The configuration above means that the default interfaces for commands
1486 1484 is curses, the interface for histedit is text and the interface for
1487 1485 selecting chunk is crecord (the best curses interface available).
1488 1486
1489 1487 Consider the following example:
1490 1488 ui.interface = curses
1491 1489 ui.interface.histedit = text
1492 1490
1493 1491 Then histedit will use the text interface and chunkselector will use
1494 1492 the default curses interface (crecord at the moment).
1495 1493 """
1496 1494 alldefaults = frozenset([b"text", b"curses"])
1497 1495
1498 1496 featureinterfaces = {
1499 1497 b"chunkselector": [
1500 1498 b"text",
1501 1499 b"curses",
1502 1500 ],
1503 1501 b"histedit": [
1504 1502 b"text",
1505 1503 b"curses",
1506 1504 ],
1507 1505 }
1508 1506
1509 1507 # Feature-specific interface
1510 1508 if feature not in featureinterfaces.keys():
1511 1509 # Programming error, not user error
1512 1510 raise ValueError(b"Unknown feature requested %s" % feature)
1513 1511
1514 1512 availableinterfaces = frozenset(featureinterfaces[feature])
1515 1513 if alldefaults > availableinterfaces:
1516 1514 # Programming error, not user error. We need a use case to
1517 1515 # define the right thing to do here.
1518 1516 raise ValueError(
1519 1517 b"Feature %s does not handle all default interfaces" % feature
1520 1518 )
1521 1519
1522 1520 if self.plain() or encoding.environ.get(b'TERM') == b'dumb':
1523 1521 return b"text"
1524 1522
1525 1523 # Default interface for all the features
1526 1524 defaultinterface = b"text"
1527 1525 i = self.config(b"ui", b"interface")
1528 1526 if i in alldefaults:
1529 1527 defaultinterface = i
1530 1528
1531 1529 choseninterface = defaultinterface
1532 1530 f = self.config(b"ui", b"interface.%s" % feature)
1533 1531 if f in availableinterfaces:
1534 1532 choseninterface = f
1535 1533
1536 1534 if i is not None and defaultinterface != i:
1537 1535 if f is not None:
1538 1536 self.warn(_(b"invalid value for ui.interface: %s\n") % (i,))
1539 1537 else:
1540 1538 self.warn(
1541 1539 _(b"invalid value for ui.interface: %s (using %s)\n")
1542 1540 % (i, choseninterface)
1543 1541 )
1544 1542 if f is not None and choseninterface != f:
1545 1543 self.warn(
1546 1544 _(b"invalid value for ui.interface.%s: %s (using %s)\n")
1547 1545 % (feature, f, choseninterface)
1548 1546 )
1549 1547
1550 1548 return choseninterface
1551 1549
1552 1550 def interactive(self):
1553 1551 """is interactive input allowed?
1554 1552
1555 1553 An interactive session is a session where input can be reasonably read
1556 1554 from `sys.stdin'. If this function returns false, any attempt to read
1557 1555 from stdin should fail with an error, unless a sensible default has been
1558 1556 specified.
1559 1557
1560 1558 Interactiveness is triggered by the value of the `ui.interactive'
1561 1559 configuration variable or - if it is unset - when `sys.stdin' points
1562 1560 to a terminal device.
1563 1561
1564 1562 This function refers to input only; for output, see `ui.formatted()'.
1565 1563 """
1566 1564 i = self.configbool(b"ui", b"interactive")
1567 1565 if i is None:
1568 1566 # some environments replace stdin without implementing isatty
1569 1567 # usually those are non-interactive
1570 1568 return self._isatty(self._fin)
1571 1569
1572 1570 return i
1573 1571
1574 1572 def termwidth(self):
1575 1573 """how wide is the terminal in columns?"""
1576 1574 if b'COLUMNS' in encoding.environ:
1577 1575 try:
1578 1576 return int(encoding.environ[b'COLUMNS'])
1579 1577 except ValueError:
1580 1578 pass
1581 1579 return scmutil.termsize(self)[0]
1582 1580
1583 1581 def formatted(self):
1584 1582 """should formatted output be used?
1585 1583
1586 1584 It is often desirable to format the output to suite the output medium.
1587 1585 Examples of this are truncating long lines or colorizing messages.
1588 1586 However, this is not often not desirable when piping output into other
1589 1587 utilities, e.g. `grep'.
1590 1588
1591 1589 Formatted output is triggered by the value of the `ui.formatted'
1592 1590 configuration variable or - if it is unset - when `sys.stdout' points
1593 1591 to a terminal device. Please note that `ui.formatted' should be
1594 1592 considered an implementation detail; it is not intended for use outside
1595 1593 Mercurial or its extensions.
1596 1594
1597 1595 This function refers to output only; for input, see `ui.interactive()'.
1598 1596 This function always returns false when in plain mode, see `ui.plain()'.
1599 1597 """
1600 1598 if self.plain():
1601 1599 return False
1602 1600
1603 1601 i = self.configbool(b"ui", b"formatted")
1604 1602 if i is None:
1605 1603 # some environments replace stdout without implementing isatty
1606 1604 # usually those are non-interactive
1607 1605 return self._isatty(self._fout)
1608 1606
1609 1607 return i
1610 1608
1611 1609 def _readline(self, prompt=b' ', promptopts=None):
1612 1610 # Replacing stdin/stdout temporarily is a hard problem on Python 3
1613 1611 # because they have to be text streams with *no buffering*. Instead,
1614 1612 # we use rawinput() only if call_readline() will be invoked by
1615 1613 # PyOS_Readline(), so no I/O will be made at Python layer.
1616 1614 usereadline = (
1617 1615 self._isatty(self._fin)
1618 1616 and self._isatty(self._fout)
1619 1617 and procutil.isstdin(self._fin)
1620 1618 and procutil.isstdout(self._fout)
1621 1619 )
1622 1620 if usereadline:
1623 1621 try:
1624 1622 # magically add command line editing support, where
1625 1623 # available
1626 1624 import readline
1627 1625
1628 1626 # force demandimport to really load the module
1629 1627 readline.read_history_file
1630 1628 # windows sometimes raises something other than ImportError
1631 1629 except Exception:
1632 1630 usereadline = False
1633 1631
1634 1632 if self._colormode == b'win32' or not usereadline:
1635 1633 if not promptopts:
1636 1634 promptopts = {}
1637 1635 self._writemsgnobuf(
1638 1636 self._fmsgout, prompt, type=b'prompt', **promptopts
1639 1637 )
1640 1638 self.flush()
1641 1639 prompt = b' '
1642 1640 else:
1643 1641 prompt = self.label(prompt, b'ui.prompt') + b' '
1644 1642
1645 1643 # prompt ' ' must exist; otherwise readline may delete entire line
1646 1644 # - http://bugs.python.org/issue12833
1647 1645 with self.timeblockedsection(b'stdio'):
1648 1646 if usereadline:
1649 1647 self.flush()
1650 1648 prompt = encoding.strfromlocal(prompt)
1651 1649 line = encoding.strtolocal(pycompat.rawinput(prompt))
1652 1650 # When stdin is in binary mode on Windows, it can cause
1653 1651 # raw_input() to emit an extra trailing carriage return
1654 1652 if pycompat.oslinesep == b'\r\n' and line.endswith(b'\r'):
1655 1653 line = line[:-1]
1656 1654 else:
1657 1655 self._fout.write(pycompat.bytestr(prompt))
1658 1656 self._fout.flush()
1659 1657 line = self._fin.readline()
1660 1658 if not line:
1661 1659 raise EOFError
1662 1660 line = line.rstrip(pycompat.oslinesep)
1663 1661
1664 1662 return line
1665 1663
1666 1664 def prompt(self, msg, default=b"y"):
1667 1665 """Prompt user with msg, read response.
1668 1666 If ui is not interactive, the default is returned.
1669 1667 """
1670 1668 return self._prompt(msg, default=default)
1671 1669
1672 1670 def _prompt(self, msg, **opts):
1673 1671 default = opts['default']
1674 1672 if not self.interactive():
1675 1673 self._writemsg(self._fmsgout, msg, b' ', type=b'prompt', **opts)
1676 1674 self._writemsg(
1677 1675 self._fmsgout, default or b'', b"\n", type=b'promptecho'
1678 1676 )
1679 1677 return default
1680 1678 try:
1681 1679 r = self._readline(prompt=msg, promptopts=opts)
1682 1680 if not r:
1683 1681 r = default
1684 1682 if self.configbool(b'ui', b'promptecho'):
1685 1683 self._writemsg(
1686 1684 self._fmsgout, r or b'', b"\n", type=b'promptecho'
1687 1685 )
1688 1686 return r
1689 1687 except EOFError:
1690 1688 raise error.ResponseExpected()
1691 1689
1692 1690 @staticmethod
1693 1691 def extractchoices(prompt):
1694 1692 """Extract prompt message and list of choices from specified prompt.
1695 1693
1696 1694 This returns tuple "(message, choices)", and "choices" is the
1697 1695 list of tuple "(response character, text without &)".
1698 1696
1699 1697 >>> ui.extractchoices(b"awake? $$ &Yes $$ &No")
1700 1698 ('awake? ', [('y', 'Yes'), ('n', 'No')])
1701 1699 >>> ui.extractchoices(b"line\\nbreak? $$ &Yes $$ &No")
1702 1700 ('line\\nbreak? ', [('y', 'Yes'), ('n', 'No')])
1703 1701 >>> ui.extractchoices(b"want lots of $$money$$?$$Ye&s$$N&o")
1704 1702 ('want lots of $$money$$?', [('s', 'Yes'), ('o', 'No')])
1705 1703 """
1706 1704
1707 1705 # Sadly, the prompt string may have been built with a filename
1708 1706 # containing "$$" so let's try to find the first valid-looking
1709 1707 # prompt to start parsing. Sadly, we also can't rely on
1710 1708 # choices containing spaces, ASCII, or basically anything
1711 1709 # except an ampersand followed by a character.
1712 1710 m = re.match(br'(?s)(.+?)\$\$([^$]*&[^ $].*)', prompt)
1713 1711 msg = m.group(1)
1714 1712 choices = [p.strip(b' ') for p in m.group(2).split(b'$$')]
1715 1713
1716 1714 def choicetuple(s):
1717 1715 ampidx = s.index(b'&')
1718 1716 return s[ampidx + 1 : ampidx + 2].lower(), s.replace(b'&', b'', 1)
1719 1717
1720 1718 return (msg, [choicetuple(s) for s in choices])
1721 1719
1722 1720 def promptchoice(self, prompt, default=0):
1723 1721 """Prompt user with a message, read response, and ensure it matches
1724 1722 one of the provided choices. The prompt is formatted as follows:
1725 1723
1726 1724 "would you like fries with that (Yn)? $$ &Yes $$ &No"
1727 1725
1728 1726 The index of the choice is returned. Responses are case
1729 1727 insensitive. If ui is not interactive, the default is
1730 1728 returned.
1731 1729 """
1732 1730
1733 1731 msg, choices = self.extractchoices(prompt)
1734 1732 resps = [r for r, t in choices]
1735 1733 while True:
1736 1734 r = self._prompt(msg, default=resps[default], choices=choices)
1737 1735 if r.lower() in resps:
1738 1736 return resps.index(r.lower())
1739 1737 # TODO: shouldn't it be a warning?
1740 1738 self._writemsg(self._fmsgout, _(b"unrecognized response\n"))
1741 1739
1742 1740 def getpass(self, prompt=None, default=None):
1743 1741 if not self.interactive():
1744 1742 return default
1745 1743 try:
1746 1744 self._writemsg(
1747 1745 self._fmsgerr,
1748 1746 prompt or _(b'password: '),
1749 1747 type=b'prompt',
1750 1748 password=True,
1751 1749 )
1752 1750 # disable getpass() only if explicitly specified. it's still valid
1753 1751 # to interact with tty even if fin is not a tty.
1754 1752 with self.timeblockedsection(b'stdio'):
1755 1753 if self.configbool(b'ui', b'nontty'):
1756 1754 l = self._fin.readline()
1757 1755 if not l:
1758 1756 raise EOFError
1759 1757 return l.rstrip(b'\n')
1760 1758 else:
1761 1759 return encoding.strtolocal(getpass.getpass(''))
1762 1760 except EOFError:
1763 1761 raise error.ResponseExpected()
1764 1762
1765 1763 def status(self, *msg, **opts):
1766 1764 """write status message to output (if ui.quiet is False)
1767 1765
1768 1766 This adds an output label of "ui.status".
1769 1767 """
1770 1768 if not self.quiet:
1771 1769 self._writemsg(self._fmsgout, type=b'status', *msg, **opts)
1772 1770
1773 1771 def warn(self, *msg, **opts):
1774 1772 """write warning message to output (stderr)
1775 1773
1776 1774 This adds an output label of "ui.warning".
1777 1775 """
1778 1776 self._writemsg(self._fmsgerr, type=b'warning', *msg, **opts)
1779 1777
1780 1778 def error(self, *msg, **opts):
1781 1779 """write error message to output (stderr)
1782 1780
1783 1781 This adds an output label of "ui.error".
1784 1782 """
1785 1783 self._writemsg(self._fmsgerr, type=b'error', *msg, **opts)
1786 1784
1787 1785 def note(self, *msg, **opts):
1788 1786 """write note to output (if ui.verbose is True)
1789 1787
1790 1788 This adds an output label of "ui.note".
1791 1789 """
1792 1790 if self.verbose:
1793 1791 self._writemsg(self._fmsgout, type=b'note', *msg, **opts)
1794 1792
1795 1793 def debug(self, *msg, **opts):
1796 1794 """write debug message to output (if ui.debugflag is True)
1797 1795
1798 1796 This adds an output label of "ui.debug".
1799 1797 """
1800 1798 if self.debugflag:
1801 1799 self._writemsg(self._fmsgout, type=b'debug', *msg, **opts)
1802 1800 self.log(b'debug', b'%s', b''.join(msg))
1803 1801
1804 1802 # Aliases to defeat check-code.
1805 1803 statusnoi18n = status
1806 1804 notenoi18n = note
1807 1805 warnnoi18n = warn
1808 1806 writenoi18n = write
1809 1807
1810 1808 def edit(
1811 1809 self,
1812 1810 text,
1813 1811 user,
1814 1812 extra=None,
1815 1813 editform=None,
1816 1814 pending=None,
1817 1815 repopath=None,
1818 1816 action=None,
1819 1817 ):
1820 1818 if action is None:
1821 1819 self.develwarn(
1822 1820 b'action is None but will soon be a required '
1823 1821 b'parameter to ui.edit()'
1824 1822 )
1825 1823 extra_defaults = {
1826 1824 b'prefix': b'editor',
1827 1825 b'suffix': b'.txt',
1828 1826 }
1829 1827 if extra is not None:
1830 1828 if extra.get(b'suffix') is not None:
1831 1829 self.develwarn(
1832 1830 b'extra.suffix is not None but will soon be '
1833 1831 b'ignored by ui.edit()'
1834 1832 )
1835 1833 extra_defaults.update(extra)
1836 1834 extra = extra_defaults
1837 1835
1838 1836 if action == b'diff':
1839 1837 suffix = b'.diff'
1840 1838 elif action:
1841 1839 suffix = b'.%s.hg.txt' % action
1842 1840 else:
1843 1841 suffix = extra[b'suffix']
1844 1842
1845 1843 rdir = None
1846 1844 if self.configbool(b'experimental', b'editortmpinhg'):
1847 1845 rdir = repopath
1848 1846 (fd, name) = pycompat.mkstemp(
1849 1847 prefix=b'hg-' + extra[b'prefix'] + b'-', suffix=suffix, dir=rdir
1850 1848 )
1851 1849 try:
1852 1850 with os.fdopen(fd, 'wb') as f:
1853 1851 f.write(util.tonativeeol(text))
1854 1852
1855 1853 environ = {b'HGUSER': user}
1856 1854 if b'transplant_source' in extra:
1857 1855 environ.update(
1858 1856 {b'HGREVISION': hex(extra[b'transplant_source'])}
1859 1857 )
1860 1858 for label in (b'intermediate-source', b'source', b'rebase_source'):
1861 1859 if label in extra:
1862 1860 environ.update({b'HGREVISION': extra[label]})
1863 1861 break
1864 1862 if editform:
1865 1863 environ.update({b'HGEDITFORM': editform})
1866 1864 if pending:
1867 1865 environ.update({b'HG_PENDING': pending})
1868 1866
1869 1867 editor = self.geteditor()
1870 1868
1871 1869 self.system(
1872 1870 b"%s \"%s\"" % (editor, name),
1873 1871 environ=environ,
1874 1872 onerr=error.CanceledError,
1875 1873 errprefix=_(b"edit failed"),
1876 1874 blockedtag=b'editor',
1877 1875 )
1878 1876
1879 1877 with open(name, 'rb') as f:
1880 1878 t = util.fromnativeeol(f.read())
1881 1879 finally:
1882 1880 os.unlink(name)
1883 1881
1884 1882 return t
1885 1883
1886 1884 def system(
1887 1885 self,
1888 1886 cmd,
1889 1887 environ=None,
1890 1888 cwd=None,
1891 1889 onerr=None,
1892 1890 errprefix=None,
1893 1891 blockedtag=None,
1894 1892 ):
1895 1893 """execute shell command with appropriate output stream. command
1896 1894 output will be redirected if fout is not stdout.
1897 1895
1898 1896 if command fails and onerr is None, return status, else raise onerr
1899 1897 object as exception.
1900 1898 """
1901 1899 if blockedtag is None:
1902 1900 # Long cmds tend to be because of an absolute path on cmd. Keep
1903 1901 # the tail end instead
1904 1902 cmdsuffix = cmd.translate(None, _keepalnum)[-85:]
1905 1903 blockedtag = b'unknown_system_' + cmdsuffix
1906 1904 out = self._fout
1907 1905 if any(s[1] for s in self._bufferstates):
1908 1906 out = self
1909 1907 with self.timeblockedsection(blockedtag):
1910 1908 rc = self._runsystem(cmd, environ=environ, cwd=cwd, out=out)
1911 1909 if rc and onerr:
1912 1910 errmsg = b'%s %s' % (
1913 1911 procutil.shellsplit(cmd)[0],
1914 1912 procutil.explainexit(rc),
1915 1913 )
1916 1914 if errprefix:
1917 1915 errmsg = b'%s: %s' % (errprefix, errmsg)
1918 1916 raise onerr(errmsg)
1919 1917 return rc
1920 1918
1921 1919 def _runsystem(self, cmd, environ, cwd, out):
1922 1920 """actually execute the given shell command (can be overridden by
1923 1921 extensions like chg)"""
1924 1922 return procutil.system(cmd, environ=environ, cwd=cwd, out=out)
1925 1923
1926 1924 def traceback(self, exc=None, force=False):
1927 1925 """print exception traceback if traceback printing enabled or forced.
1928 1926 only to call in exception handler. returns true if traceback
1929 1927 printed."""
1930 1928 if self.tracebackflag or force:
1931 1929 if exc is None:
1932 1930 exc = sys.exc_info()
1933 1931 cause = getattr(exc[1], 'cause', None)
1934 1932
1935 1933 if cause is not None:
1936 1934 causetb = traceback.format_tb(cause[2])
1937 1935 exctb = traceback.format_tb(exc[2])
1938 1936 exconly = traceback.format_exception_only(cause[0], cause[1])
1939 1937
1940 1938 # exclude frame where 'exc' was chained and rethrown from exctb
1941 1939 self.write_err(
1942 1940 b'Traceback (most recent call last):\n',
1943 1941 encoding.strtolocal(''.join(exctb[:-1])),
1944 1942 encoding.strtolocal(''.join(causetb)),
1945 1943 encoding.strtolocal(''.join(exconly)),
1946 1944 )
1947 1945 else:
1948 1946 output = traceback.format_exception(exc[0], exc[1], exc[2])
1949 1947 self.write_err(encoding.strtolocal(''.join(output)))
1950 1948 return self.tracebackflag or force
1951 1949
1952 1950 def geteditor(self):
1953 1951 '''return editor to use'''
1954 1952 if pycompat.sysplatform == b'plan9':
1955 1953 # vi is the MIPS instruction simulator on Plan 9. We
1956 1954 # instead default to E to plumb commit messages to
1957 1955 # avoid confusion.
1958 1956 editor = b'E'
1959 1957 elif pycompat.isdarwin:
1960 1958 # vi on darwin is POSIX compatible to a fault, and that includes
1961 1959 # exiting non-zero if you make any mistake when running an ex
1962 1960 # command. Proof: `vi -c ':unknown' -c ':qa'; echo $?` produces 1,
1963 1961 # while s/vi/vim/ doesn't.
1964 1962 editor = b'vim'
1965 1963 else:
1966 1964 editor = b'vi'
1967 1965 return encoding.environ.get(b"HGEDITOR") or self.config(
1968 1966 b"ui", b"editor", editor
1969 1967 )
1970 1968
1971 1969 @util.propertycache
1972 1970 def _progbar(self):
1973 1971 """setup the progbar singleton to the ui object"""
1974 1972 if (
1975 1973 self.quiet
1976 1974 or self.debugflag
1977 1975 or self.configbool(b'progress', b'disable')
1978 1976 or not progress.shouldprint(self)
1979 1977 ):
1980 1978 return None
1981 1979 return getprogbar(self)
1982 1980
1983 1981 def _progclear(self):
1984 1982 """clear progress bar output if any. use it before any output"""
1985 1983 if not haveprogbar(): # nothing loaded yet
1986 1984 return
1987 1985 if self._progbar is not None and self._progbar.printed:
1988 1986 self._progbar.clear()
1989 1987
1990 1988 def makeprogress(self, topic, unit=b"", total=None):
1991 1989 """Create a progress helper for the specified topic"""
1992 1990 if getattr(self._fmsgerr, 'structured', False):
1993 1991 # channel for machine-readable output with metadata, just send
1994 1992 # raw information
1995 1993 # TODO: consider porting some useful information (e.g. estimated
1996 1994 # time) from progbar. we might want to support update delay to
1997 1995 # reduce the cost of transferring progress messages.
1998 1996 def updatebar(topic, pos, item, unit, total):
1999 1997 self._fmsgerr.write(
2000 1998 None,
2001 1999 type=b'progress',
2002 2000 topic=topic,
2003 2001 pos=pos,
2004 2002 item=item,
2005 2003 unit=unit,
2006 2004 total=total,
2007 2005 )
2008 2006
2009 2007 elif self._progbar is not None:
2010 2008 updatebar = self._progbar.progress
2011 2009 else:
2012 2010
2013 2011 def updatebar(topic, pos, item, unit, total):
2014 2012 pass
2015 2013
2016 2014 return scmutil.progress(self, updatebar, topic, unit, total)
2017 2015
2018 2016 def getlogger(self, name):
2019 2017 """Returns a logger of the given name; or None if not registered"""
2020 2018 return self._loggers.get(name)
2021 2019
2022 2020 def setlogger(self, name, logger):
2023 2021 """Install logger which can be identified later by the given name
2024 2022
2025 2023 More than one loggers can be registered. Use extension or module
2026 2024 name to uniquely identify the logger instance.
2027 2025 """
2028 2026 self._loggers[name] = logger
2029 2027
2030 2028 def log(self, event, msgfmt, *msgargs, **opts):
2031 2029 """hook for logging facility extensions
2032 2030
2033 2031 event should be a readily-identifiable subsystem, which will
2034 2032 allow filtering.
2035 2033
2036 2034 msgfmt should be a newline-terminated format string to log, and
2037 2035 *msgargs are %-formatted into it.
2038 2036
2039 2037 **opts currently has no defined meanings.
2040 2038 """
2041 2039 if not self._loggers:
2042 2040 return
2043 2041 activeloggers = [
2044 2042 l for l in pycompat.itervalues(self._loggers) if l.tracked(event)
2045 2043 ]
2046 2044 if not activeloggers:
2047 2045 return
2048 2046 msg = msgfmt % msgargs
2049 2047 opts = pycompat.byteskwargs(opts)
2050 2048 # guard against recursion from e.g. ui.debug()
2051 2049 registeredloggers = self._loggers
2052 2050 self._loggers = {}
2053 2051 try:
2054 2052 for logger in activeloggers:
2055 2053 logger.log(self, event, msg, opts)
2056 2054 finally:
2057 2055 self._loggers = registeredloggers
2058 2056
2059 2057 def label(self, msg, label):
2060 2058 """style msg based on supplied label
2061 2059
2062 2060 If some color mode is enabled, this will add the necessary control
2063 2061 characters to apply such color. In addition, 'debug' color mode adds
2064 2062 markup showing which label affects a piece of text.
2065 2063
2066 2064 ui.write(s, 'label') is equivalent to
2067 2065 ui.write(ui.label(s, 'label')).
2068 2066 """
2069 2067 if self._colormode is not None:
2070 2068 return color.colorlabel(self, msg, label)
2071 2069 return msg
2072 2070
2073 2071 def develwarn(self, msg, stacklevel=1, config=None):
2074 2072 """issue a developer warning message
2075 2073
2076 2074 Use 'stacklevel' to report the offender some layers further up in the
2077 2075 stack.
2078 2076 """
2079 2077 if not self.configbool(b'devel', b'all-warnings'):
2080 2078 if config is None or not self.configbool(b'devel', config):
2081 2079 return
2082 2080 msg = b'devel-warn: ' + msg
2083 2081 stacklevel += 1 # get in develwarn
2084 2082 if self.tracebackflag:
2085 2083 util.debugstacktrace(msg, stacklevel, self._ferr, self._fout)
2086 2084 self.log(
2087 2085 b'develwarn',
2088 2086 b'%s at:\n%s'
2089 2087 % (msg, b''.join(util.getstackframes(stacklevel))),
2090 2088 )
2091 2089 else:
2092 2090 curframe = inspect.currentframe()
2093 2091 calframe = inspect.getouterframes(curframe, 2)
2094 2092 fname, lineno, fmsg = calframe[stacklevel][1:4]
2095 2093 fname, fmsg = pycompat.sysbytes(fname), pycompat.sysbytes(fmsg)
2096 2094 self.write_err(b'%s at: %s:%d (%s)\n' % (msg, fname, lineno, fmsg))
2097 2095 self.log(
2098 2096 b'develwarn', b'%s at: %s:%d (%s)\n', msg, fname, lineno, fmsg
2099 2097 )
2100 2098
2101 2099 # avoid cycles
2102 2100 del curframe
2103 2101 del calframe
2104 2102
2105 2103 def deprecwarn(self, msg, version, stacklevel=2):
2106 2104 """issue a deprecation warning
2107 2105
2108 2106 - msg: message explaining what is deprecated and how to upgrade,
2109 2107 - version: last version where the API will be supported,
2110 2108 """
2111 2109 if not (
2112 2110 self.configbool(b'devel', b'all-warnings')
2113 2111 or self.configbool(b'devel', b'deprec-warn')
2114 2112 ):
2115 2113 return
2116 2114 msg += (
2117 2115 b"\n(compatibility will be dropped after Mercurial-%s,"
2118 2116 b" update your code.)"
2119 2117 ) % version
2120 2118 self.develwarn(msg, stacklevel=stacklevel, config=b'deprec-warn')
2121 2119
2122 2120 def exportableenviron(self):
2123 2121 """The environment variables that are safe to export, e.g. through
2124 2122 hgweb.
2125 2123 """
2126 2124 return self._exportableenviron
2127 2125
2128 2126 @contextlib.contextmanager
2129 2127 def configoverride(self, overrides, source=b""):
2130 2128 """Context manager for temporary config overrides
2131 2129 `overrides` must be a dict of the following structure:
2132 2130 {(section, name) : value}"""
2133 2131 backups = {}
2134 2132 try:
2135 2133 for (section, name), value in overrides.items():
2136 2134 backups[(section, name)] = self.backupconfig(section, name)
2137 2135 self.setconfig(section, name, value, source)
2138 2136 yield
2139 2137 finally:
2140 2138 for __, backup in backups.items():
2141 2139 self.restoreconfig(backup)
2142 2140 # just restoring ui.quiet config to the previous value is not enough
2143 2141 # as it does not update ui.quiet class member
2144 2142 if (b'ui', b'quiet') in overrides:
2145 2143 self.fixconfig(section=b'ui')
2146 2144
2147 2145 def estimatememory(self):
2148 2146 """Provide an estimate for the available system memory in Bytes.
2149 2147
2150 2148 This can be overriden via ui.available-memory. It returns None, if
2151 2149 no estimate can be computed.
2152 2150 """
2153 2151 value = self.config(b'ui', b'available-memory')
2154 2152 if value is not None:
2155 2153 try:
2156 2154 return util.sizetoint(value)
2157 2155 except error.ParseError:
2158 2156 raise error.ConfigError(
2159 2157 _(b"ui.available-memory value is invalid ('%s')") % value
2160 2158 )
2161 2159 return util._estimatememory()
2162 2160
2163 2161
2164 2162 class paths(dict):
2165 2163 """Represents a collection of paths and their configs.
2166 2164
2167 2165 Data is initially derived from ui instances and the config files they have
2168 2166 loaded.
2169 2167 """
2170 2168
2171 2169 def __init__(self, ui):
2172 2170 dict.__init__(self)
2173 2171
2174 2172 for name, loc in ui.configitems(b'paths', ignoresub=True):
2175 2173 # No location is the same as not existing.
2176 2174 if not loc:
2177 2175 continue
2178 2176 loc, sub = ui.configsuboptions(b'paths', name)
2179 2177 self[name] = path(ui, name, rawloc=loc, suboptions=sub)
2180 2178
2181 2179 def getpath(self, name, default=None):
2182 2180 """Return a ``path`` from a string, falling back to default.
2183 2181
2184 2182 ``name`` can be a named path or locations. Locations are filesystem
2185 2183 paths or URIs.
2186 2184
2187 2185 Returns None if ``name`` is not a registered path, a URI, or a local
2188 2186 path to a repo.
2189 2187 """
2190 2188 # Only fall back to default if no path was requested.
2191 2189 if name is None:
2192 2190 if not default:
2193 2191 default = ()
2194 2192 elif not isinstance(default, (tuple, list)):
2195 2193 default = (default,)
2196 2194 for k in default:
2197 2195 try:
2198 2196 return self[k]
2199 2197 except KeyError:
2200 2198 continue
2201 2199 return None
2202 2200
2203 2201 # Most likely empty string.
2204 2202 # This may need to raise in the future.
2205 2203 if not name:
2206 2204 return None
2207 2205
2208 2206 try:
2209 2207 return self[name]
2210 2208 except KeyError:
2211 2209 # Try to resolve as a local path or URI.
2212 2210 try:
2213 2211 # We don't pass sub-options in, so no need to pass ui instance.
2214 2212 return path(None, None, rawloc=name)
2215 2213 except ValueError:
2216 2214 raise error.RepoError(_(b'repository %s does not exist') % name)
2217 2215
2218 2216
2219 2217 _pathsuboptions = {}
2220 2218
2221 2219
2222 2220 def pathsuboption(option, attr):
2223 2221 """Decorator used to declare a path sub-option.
2224 2222
2225 2223 Arguments are the sub-option name and the attribute it should set on
2226 2224 ``path`` instances.
2227 2225
2228 2226 The decorated function will receive as arguments a ``ui`` instance,
2229 2227 ``path`` instance, and the string value of this option from the config.
2230 2228 The function should return the value that will be set on the ``path``
2231 2229 instance.
2232 2230
2233 2231 This decorator can be used to perform additional verification of
2234 2232 sub-options and to change the type of sub-options.
2235 2233 """
2236 2234
2237 2235 def register(func):
2238 2236 _pathsuboptions[option] = (attr, func)
2239 2237 return func
2240 2238
2241 2239 return register
2242 2240
2243 2241
2244 2242 @pathsuboption(b'pushurl', b'pushloc')
2245 2243 def pushurlpathoption(ui, path, value):
2246 2244 u = util.url(value)
2247 2245 # Actually require a URL.
2248 2246 if not u.scheme:
2249 2247 ui.warn(_(b'(paths.%s:pushurl not a URL; ignoring)\n') % path.name)
2250 2248 return None
2251 2249
2252 2250 # Don't support the #foo syntax in the push URL to declare branch to
2253 2251 # push.
2254 2252 if u.fragment:
2255 2253 ui.warn(
2256 2254 _(
2257 2255 b'("#fragment" in paths.%s:pushurl not supported; '
2258 2256 b'ignoring)\n'
2259 2257 )
2260 2258 % path.name
2261 2259 )
2262 2260 u.fragment = None
2263 2261
2264 2262 return bytes(u)
2265 2263
2266 2264
2267 2265 @pathsuboption(b'pushrev', b'pushrev')
2268 2266 def pushrevpathoption(ui, path, value):
2269 2267 return value
2270 2268
2271 2269
2272 2270 class path(object):
2273 2271 """Represents an individual path and its configuration."""
2274 2272
2275 2273 def __init__(self, ui, name, rawloc=None, suboptions=None):
2276 2274 """Construct a path from its config options.
2277 2275
2278 2276 ``ui`` is the ``ui`` instance the path is coming from.
2279 2277 ``name`` is the symbolic name of the path.
2280 2278 ``rawloc`` is the raw location, as defined in the config.
2281 2279 ``pushloc`` is the raw locations pushes should be made to.
2282 2280
2283 2281 If ``name`` is not defined, we require that the location be a) a local
2284 2282 filesystem path with a .hg directory or b) a URL. If not,
2285 2283 ``ValueError`` is raised.
2286 2284 """
2287 2285 if not rawloc:
2288 2286 raise ValueError(b'rawloc must be defined')
2289 2287
2290 2288 # Locations may define branches via syntax <base>#<branch>.
2291 2289 u = util.url(rawloc)
2292 2290 branch = None
2293 2291 if u.fragment:
2294 2292 branch = u.fragment
2295 2293 u.fragment = None
2296 2294
2297 2295 self.url = u
2298 2296 self.branch = branch
2299 2297
2300 2298 self.name = name
2301 2299 self.rawloc = rawloc
2302 2300 self.loc = b'%s' % u
2303 2301
2304 2302 # When given a raw location but not a symbolic name, validate the
2305 2303 # location is valid.
2306 2304 if not name and not u.scheme and not self._isvalidlocalpath(self.loc):
2307 2305 raise ValueError(
2308 2306 b'location is not a URL or path to a local '
2309 2307 b'repo: %s' % rawloc
2310 2308 )
2311 2309
2312 2310 suboptions = suboptions or {}
2313 2311
2314 2312 # Now process the sub-options. If a sub-option is registered, its
2315 2313 # attribute will always be present. The value will be None if there
2316 2314 # was no valid sub-option.
2317 2315 for suboption, (attr, func) in pycompat.iteritems(_pathsuboptions):
2318 2316 if suboption not in suboptions:
2319 2317 setattr(self, attr, None)
2320 2318 continue
2321 2319
2322 2320 value = func(ui, self, suboptions[suboption])
2323 2321 setattr(self, attr, value)
2324 2322
2325 2323 def _isvalidlocalpath(self, path):
2326 2324 """Returns True if the given path is a potentially valid repository.
2327 2325 This is its own function so that extensions can change the definition of
2328 2326 'valid' in this case (like when pulling from a git repo into a hg
2329 2327 one)."""
2330 2328 try:
2331 2329 return os.path.isdir(os.path.join(path, b'.hg'))
2332 2330 # Python 2 may return TypeError. Python 3, ValueError.
2333 2331 except (TypeError, ValueError):
2334 2332 return False
2335 2333
2336 2334 @property
2337 2335 def suboptions(self):
2338 2336 """Return sub-options and their values for this path.
2339 2337
2340 2338 This is intended to be used for presentation purposes.
2341 2339 """
2342 2340 d = {}
2343 2341 for subopt, (attr, _func) in pycompat.iteritems(_pathsuboptions):
2344 2342 value = getattr(self, attr)
2345 2343 if value is not None:
2346 2344 d[subopt] = value
2347 2345 return d
2348 2346
2349 2347
2350 2348 # we instantiate one globally shared progress bar to avoid
2351 2349 # competing progress bars when multiple UI objects get created
2352 2350 _progresssingleton = None
2353 2351
2354 2352
2355 2353 def getprogbar(ui):
2356 2354 global _progresssingleton
2357 2355 if _progresssingleton is None:
2358 2356 # passing 'ui' object to the singleton is fishy,
2359 2357 # this is how the extension used to work but feel free to rework it.
2360 2358 _progresssingleton = progress.progbar(ui)
2361 2359 return _progresssingleton
2362 2360
2363 2361
2364 2362 def haveprogbar():
2365 2363 return _progresssingleton is not None
2366 2364
2367 2365
2368 2366 def _selectmsgdests(ui):
2369 2367 name = ui.config(b'ui', b'message-output')
2370 2368 if name == b'channel':
2371 2369 if ui.fmsg:
2372 2370 return ui.fmsg, ui.fmsg
2373 2371 else:
2374 2372 # fall back to ferr if channel isn't ready so that status/error
2375 2373 # messages can be printed
2376 2374 return ui.ferr, ui.ferr
2377 2375 if name == b'stdio':
2378 2376 return ui.fout, ui.ferr
2379 2377 if name == b'stderr':
2380 2378 return ui.ferr, ui.ferr
2381 2379 raise error.Abort(b'invalid ui.message-output destination: %s' % name)
2382 2380
2383 2381
2384 2382 def _writemsgwith(write, dest, *args, **opts):
2385 2383 """Write ui message with the given ui._write*() function
2386 2384
2387 2385 The specified message type is translated to 'ui.<type>' label if the dest
2388 2386 isn't a structured channel, so that the message will be colorized.
2389 2387 """
2390 2388 # TODO: maybe change 'type' to a mandatory option
2391 2389 if 'type' in opts and not getattr(dest, 'structured', False):
2392 2390 opts['label'] = opts.get('label', b'') + b' ui.%s' % opts.pop('type')
2393 2391 write(dest, *args, **opts)
@@ -1,809 +1,813 b''
1 1 # procutil.py - utility for managing processes and executable environment
2 2 #
3 3 # Copyright 2005 K. Thananchayan <thananck@yahoo.com>
4 4 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
5 5 # Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com>
6 6 #
7 7 # This software may be used and distributed according to the terms of the
8 8 # GNU General Public License version 2 or any later version.
9 9
10 10 from __future__ import absolute_import
11 11
12 12 import contextlib
13 13 import errno
14 14 import io
15 15 import os
16 16 import signal
17 17 import subprocess
18 18 import sys
19 19 import threading
20 20 import time
21 21
22 22 from ..i18n import _
23 23 from ..pycompat import (
24 24 getattr,
25 25 open,
26 26 )
27 27
28 28 from .. import (
29 29 encoding,
30 30 error,
31 31 policy,
32 32 pycompat,
33 33 )
34 34
35 35 # Import like this to keep import-checker happy
36 36 from ..utils import resourceutil
37 37
38 38 osutil = policy.importmod('osutil')
39 39
40 40 if pycompat.iswindows:
41 41 from .. import windows as platform
42 42 else:
43 43 from .. import posix as platform
44 44
45 45
46 46 def isatty(fp):
47 47 try:
48 48 return fp.isatty()
49 49 except AttributeError:
50 50 return False
51 51
52 52
53 53 class BadFile(io.RawIOBase):
54 54 """Dummy file object to simulate closed stdio behavior"""
55 55
56 56 def readinto(self, b):
57 57 raise IOError(errno.EBADF, 'Bad file descriptor')
58 58
59 59 def write(self, b):
60 60 raise IOError(errno.EBADF, 'Bad file descriptor')
61 61
62 62
63 63 class LineBufferedWrapper(object):
64 64 def __init__(self, orig):
65 65 self.orig = orig
66 66
67 67 def __getattr__(self, attr):
68 68 return getattr(self.orig, attr)
69 69
70 70 def write(self, s):
71 71 orig = self.orig
72 72 res = orig.write(s)
73 73 if s.endswith(b'\n'):
74 74 orig.flush()
75 75 return res
76 76
77 77
78 78 io.BufferedIOBase.register(LineBufferedWrapper)
79 79
80 80
81 81 def make_line_buffered(stream):
82 82 if pycompat.ispy3 and not isinstance(stream, io.BufferedIOBase):
83 83 # On Python 3, buffered streams can be expected to subclass
84 84 # BufferedIOBase. This is definitively the case for the streams
85 85 # initialized by the interpreter. For unbuffered streams, we don't need
86 86 # to emulate line buffering.
87 87 return stream
88 88 if isinstance(stream, LineBufferedWrapper):
89 89 return stream
90 90 return LineBufferedWrapper(stream)
91 91
92 92
93 93 def unwrap_line_buffered(stream):
94 94 if isinstance(stream, LineBufferedWrapper):
95 95 assert not isinstance(stream.orig, LineBufferedWrapper)
96 96 return stream.orig
97 97 return stream
98 98
99 99
100 100 class WriteAllWrapper(object):
101 101 def __init__(self, orig):
102 102 self.orig = orig
103 103
104 104 def __getattr__(self, attr):
105 105 return getattr(self.orig, attr)
106 106
107 107 def write(self, s):
108 108 write1 = self.orig.write
109 109 m = memoryview(s)
110 110 total_to_write = len(s)
111 111 total_written = 0
112 112 while total_written < total_to_write:
113 113 total_written += write1(m[total_written:])
114 114 return total_written
115 115
116 116
117 117 io.IOBase.register(WriteAllWrapper)
118 118
119 119
120 120 def _make_write_all(stream):
121 121 assert pycompat.ispy3
122 122 if isinstance(stream, WriteAllWrapper):
123 123 return stream
124 124 if isinstance(stream, io.BufferedIOBase):
125 125 # The io.BufferedIOBase.write() contract guarantees that all data is
126 126 # written.
127 127 return stream
128 128 # In general, the write() method of streams is free to write only part of
129 129 # the data.
130 130 return WriteAllWrapper(stream)
131 131
132 132
133 133 if pycompat.ispy3:
134 134 # Python 3 implements its own I/O streams. Unlike stdio of C library,
135 135 # sys.stdin/stdout/stderr may be None if underlying fd is closed.
136 136
137 137 # TODO: .buffer might not exist if std streams were replaced; we'll need
138 138 # a silly wrapper to make a bytes stream backed by a unicode one.
139 139
140 140 if sys.stdin is None:
141 141 stdin = BadFile()
142 142 else:
143 143 stdin = sys.stdin.buffer
144 144 if sys.stdout is None:
145 145 stdout = BadFile()
146 146 else:
147 147 stdout = _make_write_all(sys.stdout.buffer)
148 148 if sys.stderr is None:
149 149 stderr = BadFile()
150 150 else:
151 151 stderr = _make_write_all(sys.stderr.buffer)
152 152
153 153 if pycompat.iswindows:
154 154 # Work around Windows bugs.
155 155 stdout = platform.winstdout(stdout)
156 156 stderr = platform.winstdout(stderr)
157 157 if isatty(stdout):
158 158 # The standard library doesn't offer line-buffered binary streams.
159 159 stdout = make_line_buffered(stdout)
160 160 else:
161 161 # Python 2 uses the I/O streams provided by the C library.
162 162 stdin = sys.stdin
163 163 stdout = sys.stdout
164 164 stderr = sys.stderr
165 165 if pycompat.iswindows:
166 166 # Work around Windows bugs.
167 167 stdout = platform.winstdout(stdout)
168 168 stderr = platform.winstdout(stderr)
169 169 if isatty(stdout):
170 170 if pycompat.iswindows:
171 171 # The Windows C runtime library doesn't support line buffering.
172 172 stdout = make_line_buffered(stdout)
173 173 else:
174 174 # glibc determines buffering on first write to stdout - if we
175 175 # replace a TTY destined stdout with a pipe destined stdout (e.g.
176 176 # pager), we want line buffering.
177 177 stdout = os.fdopen(stdout.fileno(), 'wb', 1)
178 178
179 179
180 180 findexe = platform.findexe
181 181 _gethgcmd = platform.gethgcmd
182 182 getuser = platform.getuser
183 183 getpid = os.getpid
184 184 hidewindow = platform.hidewindow
185 185 readpipe = platform.readpipe
186 186 setbinary = platform.setbinary
187 187 setsignalhandler = platform.setsignalhandler
188 188 shellquote = platform.shellquote
189 189 shellsplit = platform.shellsplit
190 190 spawndetached = platform.spawndetached
191 191 sshargs = platform.sshargs
192 192 testpid = platform.testpid
193 193
194 194 try:
195 195 setprocname = osutil.setprocname
196 196 except AttributeError:
197 197 pass
198 198 try:
199 199 unblocksignal = osutil.unblocksignal
200 200 except AttributeError:
201 201 pass
202 202
203 203 closefds = pycompat.isposix
204 204
205 205
206 206 def explainexit(code):
207 207 """return a message describing a subprocess status
208 208 (codes from kill are negative - not os.system/wait encoding)"""
209 209 if code >= 0:
210 210 return _(b"exited with status %d") % code
211 211 return _(b"killed by signal %d") % -code
212 212
213 213
214 214 class _pfile(object):
215 215 """File-like wrapper for a stream opened by subprocess.Popen()"""
216 216
217 217 def __init__(self, proc, fp):
218 218 self._proc = proc
219 219 self._fp = fp
220 220
221 221 def close(self):
222 222 # unlike os.popen(), this returns an integer in subprocess coding
223 223 self._fp.close()
224 224 return self._proc.wait()
225 225
226 226 def __iter__(self):
227 227 return iter(self._fp)
228 228
229 229 def __getattr__(self, attr):
230 230 return getattr(self._fp, attr)
231 231
232 232 def __enter__(self):
233 233 return self
234 234
235 235 def __exit__(self, exc_type, exc_value, exc_tb):
236 236 self.close()
237 237
238 238
239 239 def popen(cmd, mode=b'rb', bufsize=-1):
240 240 if mode == b'rb':
241 241 return _popenreader(cmd, bufsize)
242 242 elif mode == b'wb':
243 243 return _popenwriter(cmd, bufsize)
244 244 raise error.ProgrammingError(b'unsupported mode: %r' % mode)
245 245
246 246
247 247 def _popenreader(cmd, bufsize):
248 248 p = subprocess.Popen(
249 249 tonativestr(cmd),
250 250 shell=True,
251 251 bufsize=bufsize,
252 252 close_fds=closefds,
253 253 stdout=subprocess.PIPE,
254 254 )
255 255 return _pfile(p, p.stdout)
256 256
257 257
258 258 def _popenwriter(cmd, bufsize):
259 259 p = subprocess.Popen(
260 260 tonativestr(cmd),
261 261 shell=True,
262 262 bufsize=bufsize,
263 263 close_fds=closefds,
264 264 stdin=subprocess.PIPE,
265 265 )
266 266 return _pfile(p, p.stdin)
267 267
268 268
269 269 def popen2(cmd, env=None):
270 270 # Setting bufsize to -1 lets the system decide the buffer size.
271 271 # The default for bufsize is 0, meaning unbuffered. This leads to
272 272 # poor performance on Mac OS X: http://bugs.python.org/issue4194
273 273 p = subprocess.Popen(
274 274 tonativestr(cmd),
275 275 shell=True,
276 276 bufsize=-1,
277 277 close_fds=closefds,
278 278 stdin=subprocess.PIPE,
279 279 stdout=subprocess.PIPE,
280 280 env=tonativeenv(env),
281 281 )
282 282 return p.stdin, p.stdout
283 283
284 284
285 285 def popen3(cmd, env=None):
286 286 stdin, stdout, stderr, p = popen4(cmd, env)
287 287 return stdin, stdout, stderr
288 288
289 289
290 290 def popen4(cmd, env=None, bufsize=-1):
291 291 p = subprocess.Popen(
292 292 tonativestr(cmd),
293 293 shell=True,
294 294 bufsize=bufsize,
295 295 close_fds=closefds,
296 296 stdin=subprocess.PIPE,
297 297 stdout=subprocess.PIPE,
298 298 stderr=subprocess.PIPE,
299 299 env=tonativeenv(env),
300 300 )
301 301 return p.stdin, p.stdout, p.stderr, p
302 302
303 303
304 304 def pipefilter(s, cmd):
305 305 '''filter string S through command CMD, returning its output'''
306 306 p = subprocess.Popen(
307 307 tonativestr(cmd),
308 308 shell=True,
309 309 close_fds=closefds,
310 310 stdin=subprocess.PIPE,
311 311 stdout=subprocess.PIPE,
312 312 )
313 313 pout, perr = p.communicate(s)
314 314 return pout
315 315
316 316
317 317 def tempfilter(s, cmd):
318 318 """filter string S through a pair of temporary files with CMD.
319 319 CMD is used as a template to create the real command to be run,
320 320 with the strings INFILE and OUTFILE replaced by the real names of
321 321 the temporary files generated."""
322 322 inname, outname = None, None
323 323 try:
324 324 infd, inname = pycompat.mkstemp(prefix=b'hg-filter-in-')
325 325 fp = os.fdopen(infd, 'wb')
326 326 fp.write(s)
327 327 fp.close()
328 328 outfd, outname = pycompat.mkstemp(prefix=b'hg-filter-out-')
329 329 os.close(outfd)
330 330 cmd = cmd.replace(b'INFILE', inname)
331 331 cmd = cmd.replace(b'OUTFILE', outname)
332 332 code = system(cmd)
333 333 if pycompat.sysplatform == b'OpenVMS' and code & 1:
334 334 code = 0
335 335 if code:
336 336 raise error.Abort(
337 337 _(b"command '%s' failed: %s") % (cmd, explainexit(code))
338 338 )
339 339 with open(outname, b'rb') as fp:
340 340 return fp.read()
341 341 finally:
342 342 try:
343 343 if inname:
344 344 os.unlink(inname)
345 345 except OSError:
346 346 pass
347 347 try:
348 348 if outname:
349 349 os.unlink(outname)
350 350 except OSError:
351 351 pass
352 352
353 353
354 354 _filtertable = {
355 355 b'tempfile:': tempfilter,
356 356 b'pipe:': pipefilter,
357 357 }
358 358
359 359
360 360 def filter(s, cmd):
361 361 """filter a string through a command that transforms its input to its
362 362 output"""
363 363 for name, fn in pycompat.iteritems(_filtertable):
364 364 if cmd.startswith(name):
365 365 return fn(s, cmd[len(name) :].lstrip())
366 366 return pipefilter(s, cmd)
367 367
368 368
369 369 _hgexecutable = None
370 370
371 371
372 372 def hgexecutable():
373 373 """return location of the 'hg' executable.
374 374
375 375 Defaults to $HG or 'hg' in the search path.
376 376 """
377 377 if _hgexecutable is None:
378 378 hg = encoding.environ.get(b'HG')
379 379 mainmod = sys.modules['__main__']
380 380 if hg:
381 381 _sethgexecutable(hg)
382 382 elif resourceutil.mainfrozen():
383 383 if getattr(sys, 'frozen', None) == 'macosx_app':
384 384 # Env variable set by py2app
385 385 _sethgexecutable(encoding.environ[b'EXECUTABLEPATH'])
386 386 else:
387 387 _sethgexecutable(pycompat.sysexecutable)
388 388 elif (
389 389 not pycompat.iswindows
390 390 and os.path.basename(getattr(mainmod, '__file__', '')) == 'hg'
391 391 ):
392 392 _sethgexecutable(pycompat.fsencode(mainmod.__file__))
393 393 else:
394 394 _sethgexecutable(
395 395 findexe(b'hg') or os.path.basename(pycompat.sysargv[0])
396 396 )
397 397 return _hgexecutable
398 398
399 399
400 400 def _sethgexecutable(path):
401 401 """set location of the 'hg' executable"""
402 402 global _hgexecutable
403 403 _hgexecutable = path
404 404
405 405
406 406 def _testfileno(f, stdf):
407 407 fileno = getattr(f, 'fileno', None)
408 408 try:
409 409 return fileno and fileno() == stdf.fileno()
410 410 except io.UnsupportedOperation:
411 411 return False # fileno() raised UnsupportedOperation
412 412
413 413
414 414 def isstdin(f):
415 415 return _testfileno(f, sys.__stdin__)
416 416
417 417
418 418 def isstdout(f):
419 419 return _testfileno(f, sys.__stdout__)
420 420
421 421
422 422 def protectstdio(uin, uout):
423 423 """Duplicate streams and redirect original if (uin, uout) are stdio
424 424
425 425 If uin is stdin, it's redirected to /dev/null. If uout is stdout, it's
426 426 redirected to stderr so the output is still readable.
427 427
428 428 Returns (fin, fout) which point to the original (uin, uout) fds, but
429 429 may be copy of (uin, uout). The returned streams can be considered
430 430 "owned" in that print(), exec(), etc. never reach to them.
431 431 """
432 432 uout.flush()
433 433 fin, fout = uin, uout
434 434 if _testfileno(uin, stdin):
435 435 newfd = os.dup(uin.fileno())
436 436 nullfd = os.open(os.devnull, os.O_RDONLY)
437 437 os.dup2(nullfd, uin.fileno())
438 438 os.close(nullfd)
439 439 fin = os.fdopen(newfd, 'rb')
440 440 if _testfileno(uout, stdout):
441 441 newfd = os.dup(uout.fileno())
442 442 os.dup2(stderr.fileno(), uout.fileno())
443 443 fout = os.fdopen(newfd, 'wb')
444 444 return fin, fout
445 445
446 446
447 447 def restorestdio(uin, uout, fin, fout):
448 448 """Restore (uin, uout) streams from possibly duplicated (fin, fout)"""
449 449 uout.flush()
450 450 for f, uif in [(fin, uin), (fout, uout)]:
451 451 if f is not uif:
452 452 os.dup2(f.fileno(), uif.fileno())
453 453 f.close()
454 454
455 455
456 456 def shellenviron(environ=None):
457 457 """return environ with optional override, useful for shelling out"""
458 458
459 459 def py2shell(val):
460 460 """convert python object into string that is useful to shell"""
461 461 if val is None or val is False:
462 462 return b'0'
463 463 if val is True:
464 464 return b'1'
465 465 return pycompat.bytestr(val)
466 466
467 467 env = dict(encoding.environ)
468 468 if environ:
469 469 env.update((k, py2shell(v)) for k, v in pycompat.iteritems(environ))
470 470 env[b'HG'] = hgexecutable()
471 471 return env
472 472
473 473
474 474 if pycompat.iswindows:
475 475
476 476 def shelltonative(cmd, env):
477 477 return platform.shelltocmdexe( # pytype: disable=module-attr
478 478 cmd, shellenviron(env)
479 479 )
480 480
481 481 tonativestr = encoding.strfromlocal
482 482 else:
483 483
484 484 def shelltonative(cmd, env):
485 485 return cmd
486 486
487 487 tonativestr = pycompat.identity
488 488
489 489
490 490 def tonativeenv(env):
491 491 """convert the environment from bytes to strings suitable for Popen(), etc."""
492 492 return pycompat.rapply(tonativestr, env)
493 493
494 494
495 495 def system(cmd, environ=None, cwd=None, out=None):
496 496 """enhanced shell command execution.
497 497 run with environment maybe modified, maybe in different dir.
498 498
499 499 if out is specified, it is assumed to be a file-like object that has a
500 500 write() method. stdout and stderr will be redirected to out."""
501 501 try:
502 502 stdout.flush()
503 503 except Exception:
504 504 pass
505 505 env = shellenviron(environ)
506 506 if out is None or isstdout(out):
507 507 rc = subprocess.call(
508 508 tonativestr(cmd),
509 509 shell=True,
510 510 close_fds=closefds,
511 511 env=tonativeenv(env),
512 512 cwd=pycompat.rapply(tonativestr, cwd),
513 513 )
514 514 else:
515 515 proc = subprocess.Popen(
516 516 tonativestr(cmd),
517 517 shell=True,
518 518 close_fds=closefds,
519 519 env=tonativeenv(env),
520 520 cwd=pycompat.rapply(tonativestr, cwd),
521 521 stdout=subprocess.PIPE,
522 522 stderr=subprocess.STDOUT,
523 523 )
524 524 for line in iter(proc.stdout.readline, b''):
525 525 out.write(line)
526 526 proc.wait()
527 527 rc = proc.returncode
528 528 if pycompat.sysplatform == b'OpenVMS' and rc & 1:
529 529 rc = 0
530 530 return rc
531 531
532 532
533 533 _is_gui = None
534 534
535 535
536 536 def _gui():
537 537 '''Are we running in a GUI?'''
538 538 if pycompat.isdarwin:
539 539 if b'SSH_CONNECTION' in encoding.environ:
540 540 # handle SSH access to a box where the user is logged in
541 541 return False
542 542 elif getattr(osutil, 'isgui', None):
543 543 # check if a CoreGraphics session is available
544 544 return osutil.isgui()
545 545 else:
546 546 # pure build; use a safe default
547 547 return True
548 548 else:
549 return pycompat.iswindows or encoding.environ.get(b"DISPLAY")
549 return (
550 pycompat.iswindows
551 or encoding.environ.get(b"DISPLAY")
552 or encoding.environ.get(b"WAYLAND_DISPLAY")
553 )
550 554
551 555
552 556 def gui():
553 557 global _is_gui
554 558 if _is_gui is None:
555 559 _is_gui = _gui()
556 560 return _is_gui
557 561
558 562
559 563 def hgcmd():
560 564 """Return the command used to execute current hg
561 565
562 566 This is different from hgexecutable() because on Windows we want
563 567 to avoid things opening new shell windows like batch files, so we
564 568 get either the python call or current executable.
565 569 """
566 570 if resourceutil.mainfrozen():
567 571 if getattr(sys, 'frozen', None) == 'macosx_app':
568 572 # Env variable set by py2app
569 573 return [encoding.environ[b'EXECUTABLEPATH']]
570 574 else:
571 575 return [pycompat.sysexecutable]
572 576 return _gethgcmd()
573 577
574 578
575 579 def rundetached(args, condfn):
576 580 """Execute the argument list in a detached process.
577 581
578 582 condfn is a callable which is called repeatedly and should return
579 583 True once the child process is known to have started successfully.
580 584 At this point, the child process PID is returned. If the child
581 585 process fails to start or finishes before condfn() evaluates to
582 586 True, return -1.
583 587 """
584 588 # Windows case is easier because the child process is either
585 589 # successfully starting and validating the condition or exiting
586 590 # on failure. We just poll on its PID. On Unix, if the child
587 591 # process fails to start, it will be left in a zombie state until
588 592 # the parent wait on it, which we cannot do since we expect a long
589 593 # running process on success. Instead we listen for SIGCHLD telling
590 594 # us our child process terminated.
591 595 terminated = set()
592 596
593 597 def handler(signum, frame):
594 598 terminated.add(os.wait())
595 599
596 600 prevhandler = None
597 601 SIGCHLD = getattr(signal, 'SIGCHLD', None)
598 602 if SIGCHLD is not None:
599 603 prevhandler = signal.signal(SIGCHLD, handler)
600 604 try:
601 605 pid = spawndetached(args)
602 606 while not condfn():
603 607 if (pid in terminated or not testpid(pid)) and not condfn():
604 608 return -1
605 609 time.sleep(0.1)
606 610 return pid
607 611 finally:
608 612 if prevhandler is not None:
609 613 signal.signal(signal.SIGCHLD, prevhandler)
610 614
611 615
612 616 @contextlib.contextmanager
613 617 def uninterruptible(warn):
614 618 """Inhibit SIGINT handling on a region of code.
615 619
616 620 Note that if this is called in a non-main thread, it turns into a no-op.
617 621
618 622 Args:
619 623 warn: A callable which takes no arguments, and returns True if the
620 624 previous signal handling should be restored.
621 625 """
622 626
623 627 oldsiginthandler = [signal.getsignal(signal.SIGINT)]
624 628 shouldbail = []
625 629
626 630 def disabledsiginthandler(*args):
627 631 if warn():
628 632 signal.signal(signal.SIGINT, oldsiginthandler[0])
629 633 del oldsiginthandler[0]
630 634 shouldbail.append(True)
631 635
632 636 try:
633 637 try:
634 638 signal.signal(signal.SIGINT, disabledsiginthandler)
635 639 except ValueError:
636 640 # wrong thread, oh well, we tried
637 641 del oldsiginthandler[0]
638 642 yield
639 643 finally:
640 644 if oldsiginthandler:
641 645 signal.signal(signal.SIGINT, oldsiginthandler[0])
642 646 if shouldbail:
643 647 raise KeyboardInterrupt
644 648
645 649
646 650 if pycompat.iswindows:
647 651 # no fork on Windows, but we can create a detached process
648 652 # https://msdn.microsoft.com/en-us/library/windows/desktop/ms684863.aspx
649 653 # No stdlib constant exists for this value
650 654 DETACHED_PROCESS = 0x00000008
651 655 # Following creation flags might create a console GUI window.
652 656 # Using subprocess.CREATE_NEW_CONSOLE might helps.
653 657 # See https://phab.mercurial-scm.org/D1701 for discussion
654 658 _creationflags = (
655 659 DETACHED_PROCESS
656 660 | subprocess.CREATE_NEW_PROCESS_GROUP # pytype: disable=module-attr
657 661 )
658 662
659 663 def runbgcommand(
660 664 script,
661 665 env,
662 666 shell=False,
663 667 stdout=None,
664 668 stderr=None,
665 669 ensurestart=True,
666 670 record_wait=None,
667 671 stdin_bytes=None,
668 672 ):
669 673 '''Spawn a command without waiting for it to finish.'''
670 674 # we can't use close_fds *and* redirect stdin. I'm not sure that we
671 675 # need to because the detached process has no console connection.
672 676
673 677 try:
674 678 stdin = None
675 679 if stdin_bytes is not None:
676 680 stdin = pycompat.unnamedtempfile()
677 681 stdin.write(stdin_bytes)
678 682 stdin.flush()
679 683 stdin.seek(0)
680 684
681 685 p = subprocess.Popen(
682 686 pycompat.rapply(tonativestr, script),
683 687 shell=shell,
684 688 env=tonativeenv(env),
685 689 close_fds=True,
686 690 creationflags=_creationflags,
687 691 stdin=stdin,
688 692 stdout=stdout,
689 693 stderr=stderr,
690 694 )
691 695 if record_wait is not None:
692 696 record_wait(p.wait)
693 697 finally:
694 698 if stdin is not None:
695 699 stdin.close()
696 700
697 701
698 702 else:
699 703
700 704 def runbgcommand(
701 705 cmd,
702 706 env,
703 707 shell=False,
704 708 stdout=None,
705 709 stderr=None,
706 710 ensurestart=True,
707 711 record_wait=None,
708 712 stdin_bytes=None,
709 713 ):
710 714 """Spawn a command without waiting for it to finish.
711 715
712 716
713 717 When `record_wait` is not None, the spawned process will not be fully
714 718 detached and the `record_wait` argument will be called with a the
715 719 `Subprocess.wait` function for the spawned process. This is mostly
716 720 useful for developers that need to make sure the spawned process
717 721 finished before a certain point. (eg: writing test)"""
718 722 if pycompat.isdarwin:
719 723 # avoid crash in CoreFoundation in case another thread
720 724 # calls gui() while we're calling fork().
721 725 gui()
722 726
723 727 # double-fork to completely detach from the parent process
724 728 # based on http://code.activestate.com/recipes/278731
725 729 if record_wait is None:
726 730 pid = os.fork()
727 731 if pid:
728 732 if not ensurestart:
729 733 # Even though we're not waiting on the child process,
730 734 # we still must call waitpid() on it at some point so
731 735 # it's not a zombie/defunct. This is especially relevant for
732 736 # chg since the parent process won't die anytime soon.
733 737 # We use a thread to make the overhead tiny.
734 738 def _do_wait():
735 739 os.waitpid(pid, 0)
736 740
737 741 t = threading.Thread(target=_do_wait)
738 742 t.daemon = True
739 743 t.start()
740 744 return
741 745 # Parent process
742 746 (_pid, status) = os.waitpid(pid, 0)
743 747 if os.WIFEXITED(status):
744 748 returncode = os.WEXITSTATUS(status)
745 749 else:
746 750 returncode = -(os.WTERMSIG(status))
747 751 if returncode != 0:
748 752 # The child process's return code is 0 on success, an errno
749 753 # value on failure, or 255 if we don't have a valid errno
750 754 # value.
751 755 #
752 756 # (It would be slightly nicer to return the full exception info
753 757 # over a pipe as the subprocess module does. For now it
754 758 # doesn't seem worth adding that complexity here, though.)
755 759 if returncode == 255:
756 760 returncode = errno.EINVAL
757 761 raise OSError(
758 762 returncode,
759 763 b'error running %r: %s'
760 764 % (cmd, os.strerror(returncode)),
761 765 )
762 766 return
763 767
764 768 returncode = 255
765 769 try:
766 770 if record_wait is None:
767 771 # Start a new session
768 772 os.setsid()
769 773 # connect stdin to devnull to make sure the subprocess can't
770 774 # muck up that stream for mercurial.
771 775 if stdin_bytes is None:
772 776 stdin = open(os.devnull, b'r')
773 777 else:
774 778 stdin = pycompat.unnamedtempfile()
775 779 stdin.write(stdin_bytes)
776 780 stdin.flush()
777 781 stdin.seek(0)
778 782
779 783 if stdout is None:
780 784 stdout = open(os.devnull, b'w')
781 785 if stderr is None:
782 786 stderr = open(os.devnull, b'w')
783 787
784 788 p = subprocess.Popen(
785 789 cmd,
786 790 shell=shell,
787 791 env=env,
788 792 close_fds=True,
789 793 stdin=stdin,
790 794 stdout=stdout,
791 795 stderr=stderr,
792 796 )
793 797 if record_wait is not None:
794 798 record_wait(p.wait)
795 799 returncode = 0
796 800 except EnvironmentError as ex:
797 801 returncode = ex.errno & 0xFF
798 802 if returncode == 0:
799 803 # This shouldn't happen, but just in case make sure the
800 804 # return code is never 0 here.
801 805 returncode = 255
802 806 except Exception:
803 807 returncode = 255
804 808 finally:
805 809 # mission accomplished, this child needs to exit and not
806 810 # continue the hg process here.
807 811 stdin.close()
808 812 if record_wait is None:
809 813 os._exit(returncode)
@@ -1,76 +1,73 b''
1 1 == New Features ==
2 2
3 3 * There is a new config section for templates used by hg commands. It
4 4 is called `[command-templates]`. Some existing config options have
5 5 been deprecated in favor of config options in the new
6 6 section. These are: `ui.logtemplate` to `command-templates.log`,
7 7 `ui.graphnodetemplate` to `command-templates.graphnode`,
8 8 `ui.mergemarkertemplate` to `command-templates.mergemarker`,
9 9 `ui.pre-merge-tool-output-template` to
10 10 `command-templates.pre-merge-tool-output`.
11 11
12 12 * There is a new set of config options for the template used for the
13 13 one-line commit summary displayed by various commands, such as `hg
14 14 rebase`. The main one is `command-templates.oneline-summary`. That
15 15 can be overridden per command with
16 16 `command-templates.oneline-summary.<command>`, where `<command>`
17 17 can be e.g. `rebase`. As part of this effort, the default format
18 18 from `hg rebase` was reorganized a bit.
19 19
20 20 * `hg purge` is now a core command using `--confirm` by default.
21 21
22 * `hg strip`, from the strip extension, is now a core command, `hg
23 debugstrip`. The extension remains for compatibility.
24
25 22 * `hg diff` and `hg extdiff` now support `--from <rev>` and `--to <rev>`
26 23 arguments as clearer alternatives to `-r <revs>`. `-r <revs>` has been
27 24 deprecated.
28 25
29 26 * The memory footprint per changeset during pull/unbundle
30 27 operations has been further reduced.
31 28
32 29 * There is a new internal merge tool called `internal:mergediff` (can
33 30 be set as the value for the `merge` config in the `[ui]`
34 31 section). It resolves merges the same was as `internal:merge` and
35 32 `internal:merge3`, but it shows conflicts differently. Instead of
36 33 showing 2 or 3 snapshots of the conflicting pieces of code, it
37 34 shows one snapshot and a diff. This may be useful when at least one
38 35 side of the conflict is similar to the base. The new marker style
39 36 is also supported by "premerge" as
40 37 `merge-tools.<tool>.premerge=keep-mergediff`.
41 38
42 39 * External hooks are now called with `HGPLAIN=1` preset. This has the side
43 40 effect of ignoring aliases, templates, revsetaliases, and a few other config
44 41 options in any `hg` command spawned by the hook. The previous behavior
45 42 can be restored by setting HGPLAINEXCEPT appropriately in the parent process.
46 43 See `hg help environment` for the list of items, and how to set it.
47 44
48 45 * The `branchmap` cache is updated more intelligently and can be
49 46 significantly faster for repositories with many branches and changesets.
50 47
51 48 * The `rev-branch-cache` is now updated incrementally whenever changesets
52 49 are added.
53 50
54 51
55 52 == New Experimental Features ==
56 53
57 54 * `experimental.single-head-per-branch:public-changes-only` can be used
58 55 restrict the single head check to public revision. This is useful for
59 56 overlay repository that have both a publishing and non-publishing view
60 57 of the same storage.
61 58
62 59
63 60 == Bug Fixes ==
64 61
65 62
66 63
67 64 == Backwards Compatibility Changes ==
68 65
69 66 * `--force-lock` and `--force-wlock` options on `hg debuglock` command are
70 67 renamed to `--force-free-lock` and `--force-free-wlock` respectively.
71 68
72 69
73 70 == Internal API Changes ==
74 71
75 72 * `changelog.branchinfo` is deprecated and will be removed after 5.8.
76 73 It is superseded by `changelogrevision.branchinfo`.
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
General Comments 0
You need to be logged in to leave comments. Login now