##// END OF EJS Templates
configitems: move rebase config into core...
Boris Feld -
r34832:44c4ed4a default
parent child Browse files
Show More
@@ -1,1683 +1,1669 b''
1 1 # rebase.py - rebasing feature for mercurial
2 2 #
3 3 # Copyright 2008 Stefano Tortarolo <stefano.tortarolo at gmail dot 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 '''command to move sets of revisions to a different ancestor
9 9
10 10 This extension lets you rebase changesets in an existing Mercurial
11 11 repository.
12 12
13 13 For more information:
14 14 https://mercurial-scm.org/wiki/RebaseExtension
15 15 '''
16 16
17 17 from __future__ import absolute_import
18 18
19 19 import errno
20 20 import os
21 21
22 22 from mercurial.i18n import _
23 23 from mercurial.node import (
24 24 nullid,
25 25 nullrev,
26 26 short,
27 27 )
28 28 from mercurial import (
29 29 bookmarks,
30 30 cmdutil,
31 31 commands,
32 32 copies,
33 33 destutil,
34 34 dirstateguard,
35 35 error,
36 36 extensions,
37 37 hg,
38 38 lock,
39 39 merge as mergemod,
40 40 mergeutil,
41 41 obsolete,
42 42 obsutil,
43 43 patch,
44 44 phases,
45 45 registrar,
46 46 repair,
47 47 revset,
48 48 revsetlang,
49 49 scmutil,
50 50 smartset,
51 51 util,
52 52 )
53 53
54 54 release = lock.release
55 55 templateopts = cmdutil.templateopts
56 56
57 57 # The following constants are used throughout the rebase module. The ordering of
58 58 # their values must be maintained.
59 59
60 60 # Indicates that a revision needs to be rebased
61 61 revtodo = -1
62 62 revtodostr = '-1'
63 63
64 64 # legacy revstates no longer needed in current code
65 65 # -2: nullmerge, -3: revignored, -4: revprecursor, -5: revpruned
66 66 legacystates = {'-2', '-3', '-4', '-5'}
67 67
68 68 cmdtable = {}
69 69 command = registrar.command(cmdtable)
70 70 # Note for extension authors: ONLY specify testedwith = 'ships-with-hg-core' for
71 71 # extensions which SHIP WITH MERCURIAL. Non-mainline extensions should
72 72 # be specifying the version(s) of Mercurial they are tested with, or
73 73 # leave the attribute unspecified.
74 74 testedwith = 'ships-with-hg-core'
75 75
76 configtable = {}
77 configitem = registrar.configitem(configtable)
78
79 configitem('commands', 'rebase.requiredest',
80 default=False,
81 )
82
83 configitem('experimental', 'rebaseskipobsolete',
84 default=True,
85 )
86 configitem('rebase', 'singletransaction',
87 default=False,
88 )
89
90 76 def _nothingtorebase():
91 77 return 1
92 78
93 79 def _savegraft(ctx, extra):
94 80 s = ctx.extra().get('source', None)
95 81 if s is not None:
96 82 extra['source'] = s
97 83 s = ctx.extra().get('intermediate-source', None)
98 84 if s is not None:
99 85 extra['intermediate-source'] = s
100 86
101 87 def _savebranch(ctx, extra):
102 88 extra['branch'] = ctx.branch()
103 89
104 90 def _makeextrafn(copiers):
105 91 """make an extrafn out of the given copy-functions.
106 92
107 93 A copy function takes a context and an extra dict, and mutates the
108 94 extra dict as needed based on the given context.
109 95 """
110 96 def extrafn(ctx, extra):
111 97 for c in copiers:
112 98 c(ctx, extra)
113 99 return extrafn
114 100
115 101 def _destrebase(repo, sourceset, destspace=None):
116 102 """small wrapper around destmerge to pass the right extra args
117 103
118 104 Please wrap destutil.destmerge instead."""
119 105 return destutil.destmerge(repo, action='rebase', sourceset=sourceset,
120 106 onheadcheck=False, destspace=destspace)
121 107
122 108 revsetpredicate = registrar.revsetpredicate()
123 109
124 110 @revsetpredicate('_destrebase')
125 111 def _revsetdestrebase(repo, subset, x):
126 112 # ``_rebasedefaultdest()``
127 113
128 114 # default destination for rebase.
129 115 # # XXX: Currently private because I expect the signature to change.
130 116 # # XXX: - bailing out in case of ambiguity vs returning all data.
131 117 # i18n: "_rebasedefaultdest" is a keyword
132 118 sourceset = None
133 119 if x is not None:
134 120 sourceset = revset.getset(repo, smartset.fullreposet(repo), x)
135 121 return subset & smartset.baseset([_destrebase(repo, sourceset)])
136 122
137 123 def _ctxdesc(ctx):
138 124 """short description for a context"""
139 125 desc = '%d:%s "%s"' % (ctx.rev(), ctx,
140 126 ctx.description().split('\n', 1)[0])
141 127 repo = ctx.repo()
142 128 names = []
143 129 for nsname, ns in repo.names.iteritems():
144 130 if nsname == 'branches':
145 131 continue
146 132 names.extend(ns.names(repo, ctx.node()))
147 133 if names:
148 134 desc += ' (%s)' % ' '.join(names)
149 135 return desc
150 136
151 137 class rebaseruntime(object):
152 138 """This class is a container for rebase runtime state"""
153 139 def __init__(self, repo, ui, opts=None):
154 140 if opts is None:
155 141 opts = {}
156 142
157 143 # prepared: whether we have rebasestate prepared or not. Currently it
158 144 # decides whether "self.repo" is unfiltered or not.
159 145 # The rebasestate has explicit hash to hash instructions not depending
160 146 # on visibility. If rebasestate exists (in-memory or on-disk), use
161 147 # unfiltered repo to avoid visibility issues.
162 148 # Before knowing rebasestate (i.e. when starting a new rebase (not
163 149 # --continue or --abort)), the original repo should be used so
164 150 # visibility-dependent revsets are correct.
165 151 self.prepared = False
166 152 self._repo = repo
167 153
168 154 self.ui = ui
169 155 self.opts = opts
170 156 self.originalwd = None
171 157 self.external = nullrev
172 158 # Mapping between the old revision id and either what is the new rebased
173 159 # revision or what needs to be done with the old revision. The state
174 160 # dict will be what contains most of the rebase progress state.
175 161 self.state = {}
176 162 self.activebookmark = None
177 163 self.destmap = {}
178 164 self.skipped = set()
179 165
180 166 self.collapsef = opts.get('collapse', False)
181 167 self.collapsemsg = cmdutil.logmessage(ui, opts)
182 168 self.date = opts.get('date', None)
183 169
184 170 e = opts.get('extrafn') # internal, used by e.g. hgsubversion
185 171 self.extrafns = [_savegraft]
186 172 if e:
187 173 self.extrafns = [e]
188 174
189 175 self.keepf = opts.get('keep', False)
190 176 self.keepbranchesf = opts.get('keepbranches', False)
191 177 # keepopen is not meant for use on the command line, but by
192 178 # other extensions
193 179 self.keepopen = opts.get('keepopen', False)
194 180 self.obsoletenotrebased = {}
195 181
196 182 @property
197 183 def repo(self):
198 184 if self.prepared:
199 185 return self._repo.unfiltered()
200 186 else:
201 187 return self._repo
202 188
203 189 def storestatus(self, tr=None):
204 190 """Store the current status to allow recovery"""
205 191 if tr:
206 192 tr.addfilegenerator('rebasestate', ('rebasestate',),
207 193 self._writestatus, location='plain')
208 194 else:
209 195 with self.repo.vfs("rebasestate", "w") as f:
210 196 self._writestatus(f)
211 197
212 198 def _writestatus(self, f):
213 199 repo = self.repo
214 200 assert repo.filtername is None
215 201 f.write(repo[self.originalwd].hex() + '\n')
216 202 # was "dest". we now write dest per src root below.
217 203 f.write('\n')
218 204 f.write(repo[self.external].hex() + '\n')
219 205 f.write('%d\n' % int(self.collapsef))
220 206 f.write('%d\n' % int(self.keepf))
221 207 f.write('%d\n' % int(self.keepbranchesf))
222 208 f.write('%s\n' % (self.activebookmark or ''))
223 209 destmap = self.destmap
224 210 for d, v in self.state.iteritems():
225 211 oldrev = repo[d].hex()
226 212 if v >= 0:
227 213 newrev = repo[v].hex()
228 214 else:
229 215 newrev = v
230 216 destnode = repo[destmap[d]].hex()
231 217 f.write("%s:%s:%s\n" % (oldrev, newrev, destnode))
232 218 repo.ui.debug('rebase status stored\n')
233 219
234 220 def restorestatus(self):
235 221 """Restore a previously stored status"""
236 222 self.prepared = True
237 223 repo = self.repo
238 224 assert repo.filtername is None
239 225 keepbranches = None
240 226 legacydest = None
241 227 collapse = False
242 228 external = nullrev
243 229 activebookmark = None
244 230 state = {}
245 231 destmap = {}
246 232
247 233 try:
248 234 f = repo.vfs("rebasestate")
249 235 for i, l in enumerate(f.read().splitlines()):
250 236 if i == 0:
251 237 originalwd = repo[l].rev()
252 238 elif i == 1:
253 239 # this line should be empty in newer version. but legacy
254 240 # clients may still use it
255 241 if l:
256 242 legacydest = repo[l].rev()
257 243 elif i == 2:
258 244 external = repo[l].rev()
259 245 elif i == 3:
260 246 collapse = bool(int(l))
261 247 elif i == 4:
262 248 keep = bool(int(l))
263 249 elif i == 5:
264 250 keepbranches = bool(int(l))
265 251 elif i == 6 and not (len(l) == 81 and ':' in l):
266 252 # line 6 is a recent addition, so for backwards
267 253 # compatibility check that the line doesn't look like the
268 254 # oldrev:newrev lines
269 255 activebookmark = l
270 256 else:
271 257 args = l.split(':')
272 258 oldrev = args[0]
273 259 newrev = args[1]
274 260 if newrev in legacystates:
275 261 continue
276 262 if len(args) > 2:
277 263 destnode = args[2]
278 264 else:
279 265 destnode = legacydest
280 266 destmap[repo[oldrev].rev()] = repo[destnode].rev()
281 267 if newrev in (nullid, revtodostr):
282 268 state[repo[oldrev].rev()] = revtodo
283 269 # Legacy compat special case
284 270 else:
285 271 state[repo[oldrev].rev()] = repo[newrev].rev()
286 272
287 273 except IOError as err:
288 274 if err.errno != errno.ENOENT:
289 275 raise
290 276 cmdutil.wrongtooltocontinue(repo, _('rebase'))
291 277
292 278 if keepbranches is None:
293 279 raise error.Abort(_('.hg/rebasestate is incomplete'))
294 280
295 281 skipped = set()
296 282 # recompute the set of skipped revs
297 283 if not collapse:
298 284 seen = set(destmap.values())
299 285 for old, new in sorted(state.items()):
300 286 if new != revtodo and new in seen:
301 287 skipped.add(old)
302 288 seen.add(new)
303 289 repo.ui.debug('computed skipped revs: %s\n' %
304 290 (' '.join(str(r) for r in sorted(skipped)) or None))
305 291 repo.ui.debug('rebase status resumed\n')
306 292
307 293 self.originalwd = originalwd
308 294 self.destmap = destmap
309 295 self.state = state
310 296 self.skipped = skipped
311 297 self.collapsef = collapse
312 298 self.keepf = keep
313 299 self.keepbranchesf = keepbranches
314 300 self.external = external
315 301 self.activebookmark = activebookmark
316 302
317 303 def _handleskippingobsolete(self, obsoleterevs, destmap):
318 304 """Compute structures necessary for skipping obsolete revisions
319 305
320 306 obsoleterevs: iterable of all obsolete revisions in rebaseset
321 307 destmap: {srcrev: destrev} destination revisions
322 308 """
323 309 self.obsoletenotrebased = {}
324 310 if not self.ui.configbool('experimental', 'rebaseskipobsolete'):
325 311 return
326 312 obsoleteset = set(obsoleterevs)
327 313 self.obsoletenotrebased = _computeobsoletenotrebased(self.repo,
328 314 obsoleteset, destmap)
329 315 skippedset = set(self.obsoletenotrebased)
330 316 _checkobsrebase(self.repo, self.ui, obsoleteset, skippedset)
331 317
332 318 def _prepareabortorcontinue(self, isabort):
333 319 try:
334 320 self.restorestatus()
335 321 self.collapsemsg = restorecollapsemsg(self.repo, isabort)
336 322 except error.RepoLookupError:
337 323 if isabort:
338 324 clearstatus(self.repo)
339 325 clearcollapsemsg(self.repo)
340 326 self.repo.ui.warn(_('rebase aborted (no revision is removed,'
341 327 ' only broken state is cleared)\n'))
342 328 return 0
343 329 else:
344 330 msg = _('cannot continue inconsistent rebase')
345 331 hint = _('use "hg rebase --abort" to clear broken state')
346 332 raise error.Abort(msg, hint=hint)
347 333 if isabort:
348 334 return abort(self.repo, self.originalwd, self.destmap,
349 335 self.state, activebookmark=self.activebookmark)
350 336
351 337 def _preparenewrebase(self, destmap):
352 338 if not destmap:
353 339 return _nothingtorebase()
354 340
355 341 rebaseset = destmap.keys()
356 342 allowunstable = obsolete.isenabled(self.repo, obsolete.allowunstableopt)
357 343 if (not (self.keepf or allowunstable)
358 344 and self.repo.revs('first(children(%ld) - %ld)',
359 345 rebaseset, rebaseset)):
360 346 raise error.Abort(
361 347 _("can't remove original changesets with"
362 348 " unrebased descendants"),
363 349 hint=_('use --keep to keep original changesets'))
364 350
365 351 result = buildstate(self.repo, destmap, self.collapsef)
366 352
367 353 if not result:
368 354 # Empty state built, nothing to rebase
369 355 self.ui.status(_('nothing to rebase\n'))
370 356 return _nothingtorebase()
371 357
372 358 for root in self.repo.set('roots(%ld)', rebaseset):
373 359 if not self.keepf and not root.mutable():
374 360 raise error.Abort(_("can't rebase public changeset %s")
375 361 % root,
376 362 hint=_("see 'hg help phases' for details"))
377 363
378 364 (self.originalwd, self.destmap, self.state) = result
379 365 if self.collapsef:
380 366 dests = set(self.destmap.values())
381 367 if len(dests) != 1:
382 368 raise error.Abort(
383 369 _('--collapse does not work with multiple destinations'))
384 370 destrev = next(iter(dests))
385 371 destancestors = self.repo.changelog.ancestors([destrev],
386 372 inclusive=True)
387 373 self.external = externalparent(self.repo, self.state, destancestors)
388 374
389 375 for destrev in sorted(set(destmap.values())):
390 376 dest = self.repo[destrev]
391 377 if dest.closesbranch() and not self.keepbranchesf:
392 378 self.ui.status(_('reopening closed branch head %s\n') % dest)
393 379
394 380 self.prepared = True
395 381
396 382 def _performrebase(self, tr):
397 383 repo, ui = self.repo, self.ui
398 384 if self.keepbranchesf:
399 385 # insert _savebranch at the start of extrafns so if
400 386 # there's a user-provided extrafn it can clobber branch if
401 387 # desired
402 388 self.extrafns.insert(0, _savebranch)
403 389 if self.collapsef:
404 390 branches = set()
405 391 for rev in self.state:
406 392 branches.add(repo[rev].branch())
407 393 if len(branches) > 1:
408 394 raise error.Abort(_('cannot collapse multiple named '
409 395 'branches'))
410 396
411 397 # Calculate self.obsoletenotrebased
412 398 obsrevs = _filterobsoleterevs(self.repo, self.state)
413 399 self._handleskippingobsolete(obsrevs, self.destmap)
414 400
415 401 # Keep track of the active bookmarks in order to reset them later
416 402 self.activebookmark = self.activebookmark or repo._activebookmark
417 403 if self.activebookmark:
418 404 bookmarks.deactivate(repo)
419 405
420 406 # Store the state before we begin so users can run 'hg rebase --abort'
421 407 # if we fail before the transaction closes.
422 408 self.storestatus()
423 409
424 410 cands = [k for k, v in self.state.iteritems() if v == revtodo]
425 411 total = len(cands)
426 412 pos = 0
427 413 for subset in sortsource(self.destmap):
428 414 pos = self._performrebasesubset(tr, subset, pos, total)
429 415 ui.progress(_('rebasing'), None)
430 416 ui.note(_('rebase merging completed\n'))
431 417
432 418 def _performrebasesubset(self, tr, subset, pos, total):
433 419 repo, ui, opts = self.repo, self.ui, self.opts
434 420 sortedrevs = repo.revs('sort(%ld, -topo)', subset)
435 421 for rev in sortedrevs:
436 422 dest = self.destmap[rev]
437 423 ctx = repo[rev]
438 424 desc = _ctxdesc(ctx)
439 425 if self.state[rev] == rev:
440 426 ui.status(_('already rebased %s\n') % desc)
441 427 elif rev in self.obsoletenotrebased:
442 428 succ = self.obsoletenotrebased[rev]
443 429 if succ is None:
444 430 msg = _('note: not rebasing %s, it has no '
445 431 'successor\n') % desc
446 432 else:
447 433 succdesc = _ctxdesc(repo[succ])
448 434 msg = (_('note: not rebasing %s, already in '
449 435 'destination as %s\n') % (desc, succdesc))
450 436 repo.ui.status(msg)
451 437 # Make clearrebased aware state[rev] is not a true successor
452 438 self.skipped.add(rev)
453 439 # Record rev as moved to its desired destination in self.state.
454 440 # This helps bookmark and working parent movement.
455 441 dest = max(adjustdest(repo, rev, self.destmap, self.state,
456 442 self.skipped))
457 443 self.state[rev] = dest
458 444 elif self.state[rev] == revtodo:
459 445 pos += 1
460 446 ui.status(_('rebasing %s\n') % desc)
461 447 ui.progress(_("rebasing"), pos, ("%d:%s" % (rev, ctx)),
462 448 _('changesets'), total)
463 449 p1, p2, base = defineparents(repo, rev, self.destmap,
464 450 self.state, self.skipped,
465 451 self.obsoletenotrebased)
466 452 self.storestatus(tr=tr)
467 453 storecollapsemsg(repo, self.collapsemsg)
468 454 if len(repo[None].parents()) == 2:
469 455 repo.ui.debug('resuming interrupted rebase\n')
470 456 else:
471 457 try:
472 458 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
473 459 'rebase')
474 460 stats = rebasenode(repo, rev, p1, base, self.state,
475 461 self.collapsef, dest)
476 462 if stats and stats[3] > 0:
477 463 raise error.InterventionRequired(
478 464 _('unresolved conflicts (see hg '
479 465 'resolve, then hg rebase --continue)'))
480 466 finally:
481 467 ui.setconfig('ui', 'forcemerge', '', 'rebase')
482 468 if not self.collapsef:
483 469 merging = p2 != nullrev
484 470 editform = cmdutil.mergeeditform(merging, 'rebase')
485 471 editor = cmdutil.getcommiteditor(editform=editform, **opts)
486 472 newnode = concludenode(repo, rev, p1, p2,
487 473 extrafn=_makeextrafn(self.extrafns),
488 474 editor=editor,
489 475 keepbranches=self.keepbranchesf,
490 476 date=self.date)
491 477 if newnode is None:
492 478 # If it ended up being a no-op commit, then the normal
493 479 # merge state clean-up path doesn't happen, so do it
494 480 # here. Fix issue5494
495 481 mergemod.mergestate.clean(repo)
496 482 else:
497 483 # Skip commit if we are collapsing
498 484 repo.setparents(repo[p1].node())
499 485 newnode = None
500 486 # Update the state
501 487 if newnode is not None:
502 488 self.state[rev] = repo[newnode].rev()
503 489 ui.debug('rebased as %s\n' % short(newnode))
504 490 else:
505 491 if not self.collapsef:
506 492 ui.warn(_('note: rebase of %d:%s created no changes '
507 493 'to commit\n') % (rev, ctx))
508 494 self.skipped.add(rev)
509 495 self.state[rev] = p1
510 496 ui.debug('next revision set to %s\n' % p1)
511 497 else:
512 498 ui.status(_('already rebased %s as %s\n') %
513 499 (desc, repo[self.state[rev]]))
514 500 return pos
515 501
516 502 def _finishrebase(self):
517 503 repo, ui, opts = self.repo, self.ui, self.opts
518 504 if self.collapsef and not self.keepopen:
519 505 p1, p2, _base = defineparents(repo, min(self.state), self.destmap,
520 506 self.state, self.skipped,
521 507 self.obsoletenotrebased)
522 508 editopt = opts.get('edit')
523 509 editform = 'rebase.collapse'
524 510 if self.collapsemsg:
525 511 commitmsg = self.collapsemsg
526 512 else:
527 513 commitmsg = 'Collapsed revision'
528 514 for rebased in sorted(self.state):
529 515 if rebased not in self.skipped:
530 516 commitmsg += '\n* %s' % repo[rebased].description()
531 517 editopt = True
532 518 editor = cmdutil.getcommiteditor(edit=editopt, editform=editform)
533 519 revtoreuse = max(self.state)
534 520
535 521 dsguard = None
536 522 if ui.configbool('rebase', 'singletransaction'):
537 523 dsguard = dirstateguard.dirstateguard(repo, 'rebase')
538 524 with util.acceptintervention(dsguard):
539 525 newnode = concludenode(repo, revtoreuse, p1, self.external,
540 526 commitmsg=commitmsg,
541 527 extrafn=_makeextrafn(self.extrafns),
542 528 editor=editor,
543 529 keepbranches=self.keepbranchesf,
544 530 date=self.date)
545 531 if newnode is not None:
546 532 newrev = repo[newnode].rev()
547 533 for oldrev in self.state.iterkeys():
548 534 self.state[oldrev] = newrev
549 535
550 536 if 'qtip' in repo.tags():
551 537 updatemq(repo, self.state, self.skipped, **opts)
552 538
553 539 # restore original working directory
554 540 # (we do this before stripping)
555 541 newwd = self.state.get(self.originalwd, self.originalwd)
556 542 if newwd < 0:
557 543 # original directory is a parent of rebase set root or ignored
558 544 newwd = self.originalwd
559 545 if newwd not in [c.rev() for c in repo[None].parents()]:
560 546 ui.note(_("update back to initial working directory parent\n"))
561 547 hg.updaterepo(repo, newwd, False)
562 548
563 549 collapsedas = None
564 550 if not self.keepf:
565 551 if self.collapsef:
566 552 collapsedas = newnode
567 553 clearrebased(ui, repo, self.destmap, self.state, self.skipped,
568 554 collapsedas, self.keepf)
569 555
570 556 clearstatus(repo)
571 557 clearcollapsemsg(repo)
572 558
573 559 ui.note(_("rebase completed\n"))
574 560 util.unlinkpath(repo.sjoin('undo'), ignoremissing=True)
575 561 if self.skipped:
576 562 skippedlen = len(self.skipped)
577 563 ui.note(_("%d revisions have been skipped\n") % skippedlen)
578 564
579 565 if (self.activebookmark and self.activebookmark in repo._bookmarks and
580 566 repo['.'].node() == repo._bookmarks[self.activebookmark]):
581 567 bookmarks.activate(repo, self.activebookmark)
582 568
583 569 @command('rebase',
584 570 [('s', 'source', '',
585 571 _('rebase the specified changeset and descendants'), _('REV')),
586 572 ('b', 'base', '',
587 573 _('rebase everything from branching point of specified changeset'),
588 574 _('REV')),
589 575 ('r', 'rev', [],
590 576 _('rebase these revisions'),
591 577 _('REV')),
592 578 ('d', 'dest', '',
593 579 _('rebase onto the specified changeset'), _('REV')),
594 580 ('', 'collapse', False, _('collapse the rebased changesets')),
595 581 ('m', 'message', '',
596 582 _('use text as collapse commit message'), _('TEXT')),
597 583 ('e', 'edit', False, _('invoke editor on commit messages')),
598 584 ('l', 'logfile', '',
599 585 _('read collapse commit message from file'), _('FILE')),
600 586 ('k', 'keep', False, _('keep original changesets')),
601 587 ('', 'keepbranches', False, _('keep original branch names')),
602 588 ('D', 'detach', False, _('(DEPRECATED)')),
603 589 ('i', 'interactive', False, _('(DEPRECATED)')),
604 590 ('t', 'tool', '', _('specify merge tool')),
605 591 ('c', 'continue', False, _('continue an interrupted rebase')),
606 592 ('a', 'abort', False, _('abort an interrupted rebase'))] +
607 593 templateopts,
608 594 _('[-s REV | -b REV] [-d REV] [OPTION]'))
609 595 def rebase(ui, repo, **opts):
610 596 """move changeset (and descendants) to a different branch
611 597
612 598 Rebase uses repeated merging to graft changesets from one part of
613 599 history (the source) onto another (the destination). This can be
614 600 useful for linearizing *local* changes relative to a master
615 601 development tree.
616 602
617 603 Published commits cannot be rebased (see :hg:`help phases`).
618 604 To copy commits, see :hg:`help graft`.
619 605
620 606 If you don't specify a destination changeset (``-d/--dest``), rebase
621 607 will use the same logic as :hg:`merge` to pick a destination. if
622 608 the current branch contains exactly one other head, the other head
623 609 is merged with by default. Otherwise, an explicit revision with
624 610 which to merge with must be provided. (destination changeset is not
625 611 modified by rebasing, but new changesets are added as its
626 612 descendants.)
627 613
628 614 Here are the ways to select changesets:
629 615
630 616 1. Explicitly select them using ``--rev``.
631 617
632 618 2. Use ``--source`` to select a root changeset and include all of its
633 619 descendants.
634 620
635 621 3. Use ``--base`` to select a changeset; rebase will find ancestors
636 622 and their descendants which are not also ancestors of the destination.
637 623
638 624 4. If you do not specify any of ``--rev``, ``source``, or ``--base``,
639 625 rebase will use ``--base .`` as above.
640 626
641 627 Rebase will destroy original changesets unless you use ``--keep``.
642 628 It will also move your bookmarks (even if you do).
643 629
644 630 Some changesets may be dropped if they do not contribute changes
645 631 (e.g. merges from the destination branch).
646 632
647 633 Unlike ``merge``, rebase will do nothing if you are at the branch tip of
648 634 a named branch with two heads. You will need to explicitly specify source
649 635 and/or destination.
650 636
651 637 If you need to use a tool to automate merge/conflict decisions, you
652 638 can specify one with ``--tool``, see :hg:`help merge-tools`.
653 639 As a caveat: the tool will not be used to mediate when a file was
654 640 deleted, there is no hook presently available for this.
655 641
656 642 If a rebase is interrupted to manually resolve a conflict, it can be
657 643 continued with --continue/-c or aborted with --abort/-a.
658 644
659 645 .. container:: verbose
660 646
661 647 Examples:
662 648
663 649 - move "local changes" (current commit back to branching point)
664 650 to the current branch tip after a pull::
665 651
666 652 hg rebase
667 653
668 654 - move a single changeset to the stable branch::
669 655
670 656 hg rebase -r 5f493448 -d stable
671 657
672 658 - splice a commit and all its descendants onto another part of history::
673 659
674 660 hg rebase --source c0c3 --dest 4cf9
675 661
676 662 - rebase everything on a branch marked by a bookmark onto the
677 663 default branch::
678 664
679 665 hg rebase --base myfeature --dest default
680 666
681 667 - collapse a sequence of changes into a single commit::
682 668
683 669 hg rebase --collapse -r 1520:1525 -d .
684 670
685 671 - move a named branch while preserving its name::
686 672
687 673 hg rebase -r "branch(featureX)" -d 1.3 --keepbranches
688 674
689 675 Configuration Options:
690 676
691 677 You can make rebase require a destination if you set the following config
692 678 option::
693 679
694 680 [commands]
695 681 rebase.requiredest = True
696 682
697 683 By default, rebase will close the transaction after each commit. For
698 684 performance purposes, you can configure rebase to use a single transaction
699 685 across the entire rebase. WARNING: This setting introduces a significant
700 686 risk of losing the work you've done in a rebase if the rebase aborts
701 687 unexpectedly::
702 688
703 689 [rebase]
704 690 singletransaction = True
705 691
706 692 Return Values:
707 693
708 694 Returns 0 on success, 1 if nothing to rebase or there are
709 695 unresolved conflicts.
710 696
711 697 """
712 698 rbsrt = rebaseruntime(repo, ui, opts)
713 699
714 700 with repo.wlock(), repo.lock():
715 701 # Validate input and define rebasing points
716 702 destf = opts.get('dest', None)
717 703 srcf = opts.get('source', None)
718 704 basef = opts.get('base', None)
719 705 revf = opts.get('rev', [])
720 706 # search default destination in this space
721 707 # used in the 'hg pull --rebase' case, see issue 5214.
722 708 destspace = opts.get('_destspace')
723 709 contf = opts.get('continue')
724 710 abortf = opts.get('abort')
725 711 if opts.get('interactive'):
726 712 try:
727 713 if extensions.find('histedit'):
728 714 enablehistedit = ''
729 715 except KeyError:
730 716 enablehistedit = " --config extensions.histedit="
731 717 help = "hg%s help -e histedit" % enablehistedit
732 718 msg = _("interactive history editing is supported by the "
733 719 "'histedit' extension (see \"%s\")") % help
734 720 raise error.Abort(msg)
735 721
736 722 if rbsrt.collapsemsg and not rbsrt.collapsef:
737 723 raise error.Abort(
738 724 _('message can only be specified with collapse'))
739 725
740 726 if contf or abortf:
741 727 if contf and abortf:
742 728 raise error.Abort(_('cannot use both abort and continue'))
743 729 if rbsrt.collapsef:
744 730 raise error.Abort(
745 731 _('cannot use collapse with continue or abort'))
746 732 if srcf or basef or destf:
747 733 raise error.Abort(
748 734 _('abort and continue do not allow specifying revisions'))
749 735 if abortf and opts.get('tool', False):
750 736 ui.warn(_('tool option will be ignored\n'))
751 737 if contf:
752 738 ms = mergemod.mergestate.read(repo)
753 739 mergeutil.checkunresolved(ms)
754 740
755 741 retcode = rbsrt._prepareabortorcontinue(abortf)
756 742 if retcode is not None:
757 743 return retcode
758 744 else:
759 745 destmap = _definedestmap(ui, repo, destf, srcf, basef, revf,
760 746 destspace=destspace)
761 747 retcode = rbsrt._preparenewrebase(destmap)
762 748 if retcode is not None:
763 749 return retcode
764 750
765 751 tr = None
766 752 dsguard = None
767 753
768 754 singletr = ui.configbool('rebase', 'singletransaction')
769 755 if singletr:
770 756 tr = repo.transaction('rebase')
771 757 with util.acceptintervention(tr):
772 758 if singletr:
773 759 dsguard = dirstateguard.dirstateguard(repo, 'rebase')
774 760 with util.acceptintervention(dsguard):
775 761 rbsrt._performrebase(tr)
776 762
777 763 rbsrt._finishrebase()
778 764
779 765 def _definedestmap(ui, repo, destf=None, srcf=None, basef=None, revf=None,
780 766 destspace=None):
781 767 """use revisions argument to define destmap {srcrev: destrev}"""
782 768 if revf is None:
783 769 revf = []
784 770
785 771 # destspace is here to work around issues with `hg pull --rebase` see
786 772 # issue5214 for details
787 773 if srcf and basef:
788 774 raise error.Abort(_('cannot specify both a source and a base'))
789 775 if revf and basef:
790 776 raise error.Abort(_('cannot specify both a revision and a base'))
791 777 if revf and srcf:
792 778 raise error.Abort(_('cannot specify both a revision and a source'))
793 779
794 780 cmdutil.checkunfinished(repo)
795 781 cmdutil.bailifchanged(repo)
796 782
797 783 if ui.configbool('commands', 'rebase.requiredest') and not destf:
798 784 raise error.Abort(_('you must specify a destination'),
799 785 hint=_('use: hg rebase -d REV'))
800 786
801 787 dest = None
802 788
803 789 if revf:
804 790 rebaseset = scmutil.revrange(repo, revf)
805 791 if not rebaseset:
806 792 ui.status(_('empty "rev" revision set - nothing to rebase\n'))
807 793 return None
808 794 elif srcf:
809 795 src = scmutil.revrange(repo, [srcf])
810 796 if not src:
811 797 ui.status(_('empty "source" revision set - nothing to rebase\n'))
812 798 return None
813 799 rebaseset = repo.revs('(%ld)::', src)
814 800 assert rebaseset
815 801 else:
816 802 base = scmutil.revrange(repo, [basef or '.'])
817 803 if not base:
818 804 ui.status(_('empty "base" revision set - '
819 805 "can't compute rebase set\n"))
820 806 return None
821 807 if destf:
822 808 # --base does not support multiple destinations
823 809 dest = scmutil.revsingle(repo, destf)
824 810 else:
825 811 dest = repo[_destrebase(repo, base, destspace=destspace)]
826 812 destf = str(dest)
827 813
828 814 roots = [] # selected children of branching points
829 815 bpbase = {} # {branchingpoint: [origbase]}
830 816 for b in base: # group bases by branching points
831 817 bp = repo.revs('ancestor(%d, %d)', b, dest).first()
832 818 bpbase[bp] = bpbase.get(bp, []) + [b]
833 819 if None in bpbase:
834 820 # emulate the old behavior, showing "nothing to rebase" (a better
835 821 # behavior may be abort with "cannot find branching point" error)
836 822 bpbase.clear()
837 823 for bp, bs in bpbase.iteritems(): # calculate roots
838 824 roots += list(repo.revs('children(%d) & ancestors(%ld)', bp, bs))
839 825
840 826 rebaseset = repo.revs('%ld::', roots)
841 827
842 828 if not rebaseset:
843 829 # transform to list because smartsets are not comparable to
844 830 # lists. This should be improved to honor laziness of
845 831 # smartset.
846 832 if list(base) == [dest.rev()]:
847 833 if basef:
848 834 ui.status(_('nothing to rebase - %s is both "base"'
849 835 ' and destination\n') % dest)
850 836 else:
851 837 ui.status(_('nothing to rebase - working directory '
852 838 'parent is also destination\n'))
853 839 elif not repo.revs('%ld - ::%d', base, dest):
854 840 if basef:
855 841 ui.status(_('nothing to rebase - "base" %s is '
856 842 'already an ancestor of destination '
857 843 '%s\n') %
858 844 ('+'.join(str(repo[r]) for r in base),
859 845 dest))
860 846 else:
861 847 ui.status(_('nothing to rebase - working '
862 848 'directory parent is already an '
863 849 'ancestor of destination %s\n') % dest)
864 850 else: # can it happen?
865 851 ui.status(_('nothing to rebase from %s to %s\n') %
866 852 ('+'.join(str(repo[r]) for r in base), dest))
867 853 return None
868 854
869 855 if not destf:
870 856 dest = repo[_destrebase(repo, rebaseset, destspace=destspace)]
871 857 destf = str(dest)
872 858
873 859 allsrc = revsetlang.formatspec('%ld', rebaseset)
874 860 alias = {'ALLSRC': allsrc}
875 861
876 862 if dest is None:
877 863 try:
878 864 # fast path: try to resolve dest without SRC alias
879 865 dest = scmutil.revsingle(repo, destf, localalias=alias)
880 866 except error.RepoLookupError:
881 867 if not ui.configbool('experimental', 'rebase.multidest'):
882 868 raise
883 869 # multi-dest path: resolve dest for each SRC separately
884 870 destmap = {}
885 871 for r in rebaseset:
886 872 alias['SRC'] = revsetlang.formatspec('%d', r)
887 873 # use repo.anyrevs instead of scmutil.revsingle because we
888 874 # don't want to abort if destset is empty.
889 875 destset = repo.anyrevs([destf], user=True, localalias=alias)
890 876 size = len(destset)
891 877 if size == 1:
892 878 destmap[r] = destset.first()
893 879 elif size == 0:
894 880 ui.note(_('skipping %s - empty destination\n') % repo[r])
895 881 else:
896 882 raise error.Abort(_('rebase destination for %s is not '
897 883 'unique') % repo[r])
898 884
899 885 if dest is not None:
900 886 # single-dest case: assign dest to each rev in rebaseset
901 887 destrev = dest.rev()
902 888 destmap = {r: destrev for r in rebaseset} # {srcrev: destrev}
903 889
904 890 if not destmap:
905 891 ui.status(_('nothing to rebase - empty destination\n'))
906 892 return None
907 893
908 894 return destmap
909 895
910 896 def externalparent(repo, state, destancestors):
911 897 """Return the revision that should be used as the second parent
912 898 when the revisions in state is collapsed on top of destancestors.
913 899 Abort if there is more than one parent.
914 900 """
915 901 parents = set()
916 902 source = min(state)
917 903 for rev in state:
918 904 if rev == source:
919 905 continue
920 906 for p in repo[rev].parents():
921 907 if (p.rev() not in state
922 908 and p.rev() not in destancestors):
923 909 parents.add(p.rev())
924 910 if not parents:
925 911 return nullrev
926 912 if len(parents) == 1:
927 913 return parents.pop()
928 914 raise error.Abort(_('unable to collapse on top of %s, there is more '
929 915 'than one external parent: %s') %
930 916 (max(destancestors),
931 917 ', '.join(str(p) for p in sorted(parents))))
932 918
933 919 def concludenode(repo, rev, p1, p2, commitmsg=None, editor=None, extrafn=None,
934 920 keepbranches=False, date=None):
935 921 '''Commit the wd changes with parents p1 and p2. Reuse commit info from rev
936 922 but also store useful information in extra.
937 923 Return node of committed revision.'''
938 924 dsguard = util.nullcontextmanager()
939 925 if not repo.ui.configbool('rebase', 'singletransaction'):
940 926 dsguard = dirstateguard.dirstateguard(repo, 'rebase')
941 927 with dsguard:
942 928 repo.setparents(repo[p1].node(), repo[p2].node())
943 929 ctx = repo[rev]
944 930 if commitmsg is None:
945 931 commitmsg = ctx.description()
946 932 keepbranch = keepbranches and repo[p1].branch() != ctx.branch()
947 933 extra = {'rebase_source': ctx.hex()}
948 934 if extrafn:
949 935 extrafn(ctx, extra)
950 936
951 937 destphase = max(ctx.phase(), phases.draft)
952 938 overrides = {('phases', 'new-commit'): destphase}
953 939 with repo.ui.configoverride(overrides, 'rebase'):
954 940 if keepbranch:
955 941 repo.ui.setconfig('ui', 'allowemptycommit', True)
956 942 # Commit might fail if unresolved files exist
957 943 if date is None:
958 944 date = ctx.date()
959 945 newnode = repo.commit(text=commitmsg, user=ctx.user(),
960 946 date=date, extra=extra, editor=editor)
961 947
962 948 repo.dirstate.setbranch(repo[newnode].branch())
963 949 return newnode
964 950
965 951 def rebasenode(repo, rev, p1, base, state, collapse, dest):
966 952 'Rebase a single revision rev on top of p1 using base as merge ancestor'
967 953 # Merge phase
968 954 # Update to destination and merge it with local
969 955 if repo['.'].rev() != p1:
970 956 repo.ui.debug(" update to %d:%s\n" % (p1, repo[p1]))
971 957 mergemod.update(repo, p1, False, True)
972 958 else:
973 959 repo.ui.debug(" already in destination\n")
974 960 repo.dirstate.write(repo.currenttransaction())
975 961 repo.ui.debug(" merge against %d:%s\n" % (rev, repo[rev]))
976 962 if base is not None:
977 963 repo.ui.debug(" detach base %d:%s\n" % (base, repo[base]))
978 964 # When collapsing in-place, the parent is the common ancestor, we
979 965 # have to allow merging with it.
980 966 wctx = repo[None]
981 967 stats = mergemod.update(repo, rev, True, True, base, collapse,
982 968 labels=['dest', 'source'])
983 969 if collapse:
984 970 copies.duplicatecopies(repo, wctx, rev, dest)
985 971 else:
986 972 # If we're not using --collapse, we need to
987 973 # duplicate copies between the revision we're
988 974 # rebasing and its first parent, but *not*
989 975 # duplicate any copies that have already been
990 976 # performed in the destination.
991 977 p1rev = repo[rev].p1().rev()
992 978 copies.duplicatecopies(repo, wctx, rev, p1rev, skiprev=dest)
993 979 return stats
994 980
995 981 def adjustdest(repo, rev, destmap, state, skipped):
996 982 """adjust rebase destination given the current rebase state
997 983
998 984 rev is what is being rebased. Return a list of two revs, which are the
999 985 adjusted destinations for rev's p1 and p2, respectively. If a parent is
1000 986 nullrev, return dest without adjustment for it.
1001 987
1002 988 For example, when doing rebasing B+E to F, C to G, rebase will first move B
1003 989 to B1, and E's destination will be adjusted from F to B1.
1004 990
1005 991 B1 <- written during rebasing B
1006 992 |
1007 993 F <- original destination of B, E
1008 994 |
1009 995 | E <- rev, which is being rebased
1010 996 | |
1011 997 | D <- prev, one parent of rev being checked
1012 998 | |
1013 999 | x <- skipped, ex. no successor or successor in (::dest)
1014 1000 | |
1015 1001 | C <- rebased as C', different destination
1016 1002 | |
1017 1003 | B <- rebased as B1 C'
1018 1004 |/ |
1019 1005 A G <- destination of C, different
1020 1006
1021 1007 Another example about merge changeset, rebase -r C+G+H -d K, rebase will
1022 1008 first move C to C1, G to G1, and when it's checking H, the adjusted
1023 1009 destinations will be [C1, G1].
1024 1010
1025 1011 H C1 G1
1026 1012 /| | /
1027 1013 F G |/
1028 1014 K | | -> K
1029 1015 | C D |
1030 1016 | |/ |
1031 1017 | B | ...
1032 1018 |/ |/
1033 1019 A A
1034 1020
1035 1021 Besides, adjust dest according to existing rebase information. For example,
1036 1022
1037 1023 B C D B needs to be rebased on top of C, C needs to be rebased on top
1038 1024 \|/ of D. We will rebase C first.
1039 1025 A
1040 1026
1041 1027 C' After rebasing C, when considering B's destination, use C'
1042 1028 | instead of the original C.
1043 1029 B D
1044 1030 \ /
1045 1031 A
1046 1032 """
1047 1033 # pick already rebased revs with same dest from state as interesting source
1048 1034 dest = destmap[rev]
1049 1035 source = [s for s, d in state.items()
1050 1036 if d > 0 and destmap[s] == dest and s not in skipped]
1051 1037
1052 1038 result = []
1053 1039 for prev in repo.changelog.parentrevs(rev):
1054 1040 adjusted = dest
1055 1041 if prev != nullrev:
1056 1042 candidate = repo.revs('max(%ld and (::%d))', source, prev).first()
1057 1043 if candidate is not None:
1058 1044 adjusted = state[candidate]
1059 1045 if adjusted == dest and dest in state:
1060 1046 adjusted = state[dest]
1061 1047 if adjusted == revtodo:
1062 1048 # sortsource should produce an order that makes this impossible
1063 1049 raise error.ProgrammingError(
1064 1050 'rev %d should be rebased already at this time' % dest)
1065 1051 result.append(adjusted)
1066 1052 return result
1067 1053
1068 1054 def _checkobsrebase(repo, ui, rebaseobsrevs, rebaseobsskipped):
1069 1055 """
1070 1056 Abort if rebase will create divergence or rebase is noop because of markers
1071 1057
1072 1058 `rebaseobsrevs`: set of obsolete revision in source
1073 1059 `rebaseobsskipped`: set of revisions from source skipped because they have
1074 1060 successors in destination
1075 1061 """
1076 1062 # Obsolete node with successors not in dest leads to divergence
1077 1063 divergenceok = ui.configbool('experimental',
1078 1064 'allowdivergence')
1079 1065 divergencebasecandidates = rebaseobsrevs - rebaseobsskipped
1080 1066
1081 1067 if divergencebasecandidates and not divergenceok:
1082 1068 divhashes = (str(repo[r])
1083 1069 for r in divergencebasecandidates)
1084 1070 msg = _("this rebase will cause "
1085 1071 "divergences from: %s")
1086 1072 h = _("to force the rebase please set "
1087 1073 "experimental.allowdivergence=True")
1088 1074 raise error.Abort(msg % (",".join(divhashes),), hint=h)
1089 1075
1090 1076 def successorrevs(unfi, rev):
1091 1077 """yield revision numbers for successors of rev"""
1092 1078 assert unfi.filtername is None
1093 1079 nodemap = unfi.changelog.nodemap
1094 1080 for s in obsutil.allsuccessors(unfi.obsstore, [unfi[rev].node()]):
1095 1081 if s in nodemap:
1096 1082 yield nodemap[s]
1097 1083
1098 1084 def defineparents(repo, rev, destmap, state, skipped, obsskipped):
1099 1085 """Return new parents and optionally a merge base for rev being rebased
1100 1086
1101 1087 The destination specified by "dest" cannot always be used directly because
1102 1088 previously rebase result could affect destination. For example,
1103 1089
1104 1090 D E rebase -r C+D+E -d B
1105 1091 |/ C will be rebased to C'
1106 1092 B C D's new destination will be C' instead of B
1107 1093 |/ E's new destination will be C' instead of B
1108 1094 A
1109 1095
1110 1096 The new parents of a merge is slightly more complicated. See the comment
1111 1097 block below.
1112 1098 """
1113 1099 # use unfiltered changelog since successorrevs may return filtered nodes
1114 1100 assert repo.filtername is None
1115 1101 cl = repo.changelog
1116 1102 def isancestor(a, b):
1117 1103 # take revision numbers instead of nodes
1118 1104 if a == b:
1119 1105 return True
1120 1106 elif a > b:
1121 1107 return False
1122 1108 return cl.isancestor(cl.node(a), cl.node(b))
1123 1109
1124 1110 dest = destmap[rev]
1125 1111 oldps = repo.changelog.parentrevs(rev) # old parents
1126 1112 newps = [nullrev, nullrev] # new parents
1127 1113 dests = adjustdest(repo, rev, destmap, state, skipped)
1128 1114 bases = list(oldps) # merge base candidates, initially just old parents
1129 1115
1130 1116 if all(r == nullrev for r in oldps[1:]):
1131 1117 # For non-merge changeset, just move p to adjusted dest as requested.
1132 1118 newps[0] = dests[0]
1133 1119 else:
1134 1120 # For merge changeset, if we move p to dests[i] unconditionally, both
1135 1121 # parents may change and the end result looks like "the merge loses a
1136 1122 # parent", which is a surprise. This is a limit because "--dest" only
1137 1123 # accepts one dest per src.
1138 1124 #
1139 1125 # Therefore, only move p with reasonable conditions (in this order):
1140 1126 # 1. use dest, if dest is a descendent of (p or one of p's successors)
1141 1127 # 2. use p's rebased result, if p is rebased (state[p] > 0)
1142 1128 #
1143 1129 # Comparing with adjustdest, the logic here does some additional work:
1144 1130 # 1. decide which parents will not be moved towards dest
1145 1131 # 2. if the above decision is "no", should a parent still be moved
1146 1132 # because it was rebased?
1147 1133 #
1148 1134 # For example:
1149 1135 #
1150 1136 # C # "rebase -r C -d D" is an error since none of the parents
1151 1137 # /| # can be moved. "rebase -r B+C -d D" will move C's parent
1152 1138 # A B D # B (using rule "2."), since B will be rebased.
1153 1139 #
1154 1140 # The loop tries to be not rely on the fact that a Mercurial node has
1155 1141 # at most 2 parents.
1156 1142 for i, p in enumerate(oldps):
1157 1143 np = p # new parent
1158 1144 if any(isancestor(x, dests[i]) for x in successorrevs(repo, p)):
1159 1145 np = dests[i]
1160 1146 elif p in state and state[p] > 0:
1161 1147 np = state[p]
1162 1148
1163 1149 # "bases" only record "special" merge bases that cannot be
1164 1150 # calculated from changelog DAG (i.e. isancestor(p, np) is False).
1165 1151 # For example:
1166 1152 #
1167 1153 # B' # rebase -s B -d D, when B was rebased to B'. dest for C
1168 1154 # | C # is B', but merge base for C is B, instead of
1169 1155 # D | # changelog.ancestor(C, B') == A. If changelog DAG and
1170 1156 # | B # "state" edges are merged (so there will be an edge from
1171 1157 # |/ # B to B'), the merge base is still ancestor(C, B') in
1172 1158 # A # the merged graph.
1173 1159 #
1174 1160 # Also see https://bz.mercurial-scm.org/show_bug.cgi?id=1950#c8
1175 1161 # which uses "virtual null merge" to explain this situation.
1176 1162 if isancestor(p, np):
1177 1163 bases[i] = nullrev
1178 1164
1179 1165 # If one parent becomes an ancestor of the other, drop the ancestor
1180 1166 for j, x in enumerate(newps[:i]):
1181 1167 if x == nullrev:
1182 1168 continue
1183 1169 if isancestor(np, x): # CASE-1
1184 1170 np = nullrev
1185 1171 elif isancestor(x, np): # CASE-2
1186 1172 newps[j] = np
1187 1173 np = nullrev
1188 1174 # New parents forming an ancestor relationship does not
1189 1175 # mean the old parents have a similar relationship. Do not
1190 1176 # set bases[x] to nullrev.
1191 1177 bases[j], bases[i] = bases[i], bases[j]
1192 1178
1193 1179 newps[i] = np
1194 1180
1195 1181 # "rebasenode" updates to new p1, and the old p1 will be used as merge
1196 1182 # base. If only p2 changes, merging using unchanged p1 as merge base is
1197 1183 # suboptimal. Therefore swap parents to make the merge sane.
1198 1184 if newps[1] != nullrev and oldps[0] == newps[0]:
1199 1185 assert len(newps) == 2 and len(oldps) == 2
1200 1186 newps.reverse()
1201 1187 bases.reverse()
1202 1188
1203 1189 # No parent change might be an error because we fail to make rev a
1204 1190 # descendent of requested dest. This can happen, for example:
1205 1191 #
1206 1192 # C # rebase -r C -d D
1207 1193 # /| # None of A and B will be changed to D and rebase fails.
1208 1194 # A B D
1209 1195 if set(newps) == set(oldps) and dest not in newps:
1210 1196 raise error.Abort(_('cannot rebase %d:%s without '
1211 1197 'moving at least one of its parents')
1212 1198 % (rev, repo[rev]))
1213 1199
1214 1200 # Source should not be ancestor of dest. The check here guarantees it's
1215 1201 # impossible. With multi-dest, the initial check does not cover complex
1216 1202 # cases since we don't have abstractions to dry-run rebase cheaply.
1217 1203 if any(p != nullrev and isancestor(rev, p) for p in newps):
1218 1204 raise error.Abort(_('source is ancestor of destination'))
1219 1205
1220 1206 # "rebasenode" updates to new p1, use the corresponding merge base.
1221 1207 if bases[0] != nullrev:
1222 1208 base = bases[0]
1223 1209 else:
1224 1210 base = None
1225 1211
1226 1212 # Check if the merge will contain unwanted changes. That may happen if
1227 1213 # there are multiple special (non-changelog ancestor) merge bases, which
1228 1214 # cannot be handled well by the 3-way merge algorithm. For example:
1229 1215 #
1230 1216 # F
1231 1217 # /|
1232 1218 # D E # "rebase -r D+E+F -d Z", when rebasing F, if "D" was chosen
1233 1219 # | | # as merge base, the difference between D and F will include
1234 1220 # B C # C, so the rebased F will contain C surprisingly. If "E" was
1235 1221 # |/ # chosen, the rebased F will contain B.
1236 1222 # A Z
1237 1223 #
1238 1224 # But our merge base candidates (D and E in above case) could still be
1239 1225 # better than the default (ancestor(F, Z) == null). Therefore still
1240 1226 # pick one (so choose p1 above).
1241 1227 if sum(1 for b in bases if b != nullrev) > 1:
1242 1228 unwanted = [None, None] # unwanted[i]: unwanted revs if choose bases[i]
1243 1229 for i, base in enumerate(bases):
1244 1230 if base == nullrev:
1245 1231 continue
1246 1232 # Revisions in the side (not chosen as merge base) branch that
1247 1233 # might contain "surprising" contents
1248 1234 siderevs = list(repo.revs('((%ld-%d) %% (%d+%d))',
1249 1235 bases, base, base, dest))
1250 1236
1251 1237 # If those revisions are covered by rebaseset, the result is good.
1252 1238 # A merge in rebaseset would be considered to cover its ancestors.
1253 1239 if siderevs:
1254 1240 rebaseset = [r for r, d in state.items()
1255 1241 if d > 0 and r not in obsskipped]
1256 1242 merges = [r for r in rebaseset
1257 1243 if cl.parentrevs(r)[1] != nullrev]
1258 1244 unwanted[i] = list(repo.revs('%ld - (::%ld) - %ld',
1259 1245 siderevs, merges, rebaseset))
1260 1246
1261 1247 # Choose a merge base that has a minimal number of unwanted revs.
1262 1248 l, i = min((len(revs), i)
1263 1249 for i, revs in enumerate(unwanted) if revs is not None)
1264 1250 base = bases[i]
1265 1251
1266 1252 # newps[0] should match merge base if possible. Currently, if newps[i]
1267 1253 # is nullrev, the only case is newps[i] and newps[j] (j < i), one is
1268 1254 # the other's ancestor. In that case, it's fine to not swap newps here.
1269 1255 # (see CASE-1 and CASE-2 above)
1270 1256 if i != 0 and newps[i] != nullrev:
1271 1257 newps[0], newps[i] = newps[i], newps[0]
1272 1258
1273 1259 # The merge will include unwanted revisions. Abort now. Revisit this if
1274 1260 # we have a more advanced merge algorithm that handles multiple bases.
1275 1261 if l > 0:
1276 1262 unwanteddesc = _(' or ').join(
1277 1263 (', '.join('%d:%s' % (r, repo[r]) for r in revs)
1278 1264 for revs in unwanted if revs is not None))
1279 1265 raise error.Abort(
1280 1266 _('rebasing %d:%s will include unwanted changes from %s')
1281 1267 % (rev, repo[rev], unwanteddesc))
1282 1268
1283 1269 repo.ui.debug(" future parents are %d and %d\n" % tuple(newps))
1284 1270
1285 1271 return newps[0], newps[1], base
1286 1272
1287 1273 def isagitpatch(repo, patchname):
1288 1274 'Return true if the given patch is in git format'
1289 1275 mqpatch = os.path.join(repo.mq.path, patchname)
1290 1276 for line in patch.linereader(file(mqpatch, 'rb')):
1291 1277 if line.startswith('diff --git'):
1292 1278 return True
1293 1279 return False
1294 1280
1295 1281 def updatemq(repo, state, skipped, **opts):
1296 1282 'Update rebased mq patches - finalize and then import them'
1297 1283 mqrebase = {}
1298 1284 mq = repo.mq
1299 1285 original_series = mq.fullseries[:]
1300 1286 skippedpatches = set()
1301 1287
1302 1288 for p in mq.applied:
1303 1289 rev = repo[p.node].rev()
1304 1290 if rev in state:
1305 1291 repo.ui.debug('revision %d is an mq patch (%s), finalize it.\n' %
1306 1292 (rev, p.name))
1307 1293 mqrebase[rev] = (p.name, isagitpatch(repo, p.name))
1308 1294 else:
1309 1295 # Applied but not rebased, not sure this should happen
1310 1296 skippedpatches.add(p.name)
1311 1297
1312 1298 if mqrebase:
1313 1299 mq.finish(repo, mqrebase.keys())
1314 1300
1315 1301 # We must start import from the newest revision
1316 1302 for rev in sorted(mqrebase, reverse=True):
1317 1303 if rev not in skipped:
1318 1304 name, isgit = mqrebase[rev]
1319 1305 repo.ui.note(_('updating mq patch %s to %s:%s\n') %
1320 1306 (name, state[rev], repo[state[rev]]))
1321 1307 mq.qimport(repo, (), patchname=name, git=isgit,
1322 1308 rev=[str(state[rev])])
1323 1309 else:
1324 1310 # Rebased and skipped
1325 1311 skippedpatches.add(mqrebase[rev][0])
1326 1312
1327 1313 # Patches were either applied and rebased and imported in
1328 1314 # order, applied and removed or unapplied. Discard the removed
1329 1315 # ones while preserving the original series order and guards.
1330 1316 newseries = [s for s in original_series
1331 1317 if mq.guard_re.split(s, 1)[0] not in skippedpatches]
1332 1318 mq.fullseries[:] = newseries
1333 1319 mq.seriesdirty = True
1334 1320 mq.savedirty()
1335 1321
1336 1322 def storecollapsemsg(repo, collapsemsg):
1337 1323 'Store the collapse message to allow recovery'
1338 1324 collapsemsg = collapsemsg or ''
1339 1325 f = repo.vfs("last-message.txt", "w")
1340 1326 f.write("%s\n" % collapsemsg)
1341 1327 f.close()
1342 1328
1343 1329 def clearcollapsemsg(repo):
1344 1330 'Remove collapse message file'
1345 1331 repo.vfs.unlinkpath("last-message.txt", ignoremissing=True)
1346 1332
1347 1333 def restorecollapsemsg(repo, isabort):
1348 1334 'Restore previously stored collapse message'
1349 1335 try:
1350 1336 f = repo.vfs("last-message.txt")
1351 1337 collapsemsg = f.readline().strip()
1352 1338 f.close()
1353 1339 except IOError as err:
1354 1340 if err.errno != errno.ENOENT:
1355 1341 raise
1356 1342 if isabort:
1357 1343 # Oh well, just abort like normal
1358 1344 collapsemsg = ''
1359 1345 else:
1360 1346 raise error.Abort(_('missing .hg/last-message.txt for rebase'))
1361 1347 return collapsemsg
1362 1348
1363 1349 def clearstatus(repo):
1364 1350 'Remove the status files'
1365 1351 # Make sure the active transaction won't write the state file
1366 1352 tr = repo.currenttransaction()
1367 1353 if tr:
1368 1354 tr.removefilegenerator('rebasestate')
1369 1355 repo.vfs.unlinkpath("rebasestate", ignoremissing=True)
1370 1356
1371 1357 def needupdate(repo, state):
1372 1358 '''check whether we should `update --clean` away from a merge, or if
1373 1359 somehow the working dir got forcibly updated, e.g. by older hg'''
1374 1360 parents = [p.rev() for p in repo[None].parents()]
1375 1361
1376 1362 # Are we in a merge state at all?
1377 1363 if len(parents) < 2:
1378 1364 return False
1379 1365
1380 1366 # We should be standing on the first as-of-yet unrebased commit.
1381 1367 firstunrebased = min([old for old, new in state.iteritems()
1382 1368 if new == nullrev])
1383 1369 if firstunrebased in parents:
1384 1370 return True
1385 1371
1386 1372 return False
1387 1373
1388 1374 def abort(repo, originalwd, destmap, state, activebookmark=None):
1389 1375 '''Restore the repository to its original state. Additional args:
1390 1376
1391 1377 activebookmark: the name of the bookmark that should be active after the
1392 1378 restore'''
1393 1379
1394 1380 try:
1395 1381 # If the first commits in the rebased set get skipped during the rebase,
1396 1382 # their values within the state mapping will be the dest rev id. The
1397 1383 # dstates list must must not contain the dest rev (issue4896)
1398 1384 dstates = [s for r, s in state.items() if s >= 0 and s != destmap[r]]
1399 1385 immutable = [d for d in dstates if not repo[d].mutable()]
1400 1386 cleanup = True
1401 1387 if immutable:
1402 1388 repo.ui.warn(_("warning: can't clean up public changesets %s\n")
1403 1389 % ', '.join(str(repo[r]) for r in immutable),
1404 1390 hint=_("see 'hg help phases' for details"))
1405 1391 cleanup = False
1406 1392
1407 1393 descendants = set()
1408 1394 if dstates:
1409 1395 descendants = set(repo.changelog.descendants(dstates))
1410 1396 if descendants - set(dstates):
1411 1397 repo.ui.warn(_("warning: new changesets detected on destination "
1412 1398 "branch, can't strip\n"))
1413 1399 cleanup = False
1414 1400
1415 1401 if cleanup:
1416 1402 shouldupdate = False
1417 1403 rebased = [s for r, s in state.items()
1418 1404 if s >= 0 and s != destmap[r]]
1419 1405 if rebased:
1420 1406 strippoints = [
1421 1407 c.node() for c in repo.set('roots(%ld)', rebased)]
1422 1408
1423 1409 updateifonnodes = set(rebased)
1424 1410 updateifonnodes.update(destmap.values())
1425 1411 updateifonnodes.add(originalwd)
1426 1412 shouldupdate = repo['.'].rev() in updateifonnodes
1427 1413
1428 1414 # Update away from the rebase if necessary
1429 1415 if shouldupdate or needupdate(repo, state):
1430 1416 mergemod.update(repo, originalwd, False, True)
1431 1417
1432 1418 # Strip from the first rebased revision
1433 1419 if rebased:
1434 1420 # no backup of rebased cset versions needed
1435 1421 repair.strip(repo.ui, repo, strippoints)
1436 1422
1437 1423 if activebookmark and activebookmark in repo._bookmarks:
1438 1424 bookmarks.activate(repo, activebookmark)
1439 1425
1440 1426 finally:
1441 1427 clearstatus(repo)
1442 1428 clearcollapsemsg(repo)
1443 1429 repo.ui.warn(_('rebase aborted\n'))
1444 1430 return 0
1445 1431
1446 1432 def sortsource(destmap):
1447 1433 """yield source revisions in an order that we only rebase things once
1448 1434
1449 1435 If source and destination overlaps, we should filter out revisions
1450 1436 depending on other revisions which hasn't been rebased yet.
1451 1437
1452 1438 Yield a sorted list of revisions each time.
1453 1439
1454 1440 For example, when rebasing A to B, B to C. This function yields [B], then
1455 1441 [A], indicating B needs to be rebased first.
1456 1442
1457 1443 Raise if there is a cycle so the rebase is impossible.
1458 1444 """
1459 1445 srcset = set(destmap)
1460 1446 while srcset:
1461 1447 srclist = sorted(srcset)
1462 1448 result = []
1463 1449 for r in srclist:
1464 1450 if destmap[r] not in srcset:
1465 1451 result.append(r)
1466 1452 if not result:
1467 1453 raise error.Abort(_('source and destination form a cycle'))
1468 1454 srcset -= set(result)
1469 1455 yield result
1470 1456
1471 1457 def buildstate(repo, destmap, collapse):
1472 1458 '''Define which revisions are going to be rebased and where
1473 1459
1474 1460 repo: repo
1475 1461 destmap: {srcrev: destrev}
1476 1462 '''
1477 1463 rebaseset = destmap.keys()
1478 1464 originalwd = repo['.'].rev()
1479 1465
1480 1466 # This check isn't strictly necessary, since mq detects commits over an
1481 1467 # applied patch. But it prevents messing up the working directory when
1482 1468 # a partially completed rebase is blocked by mq.
1483 1469 if 'qtip' in repo.tags():
1484 1470 mqapplied = set(repo[s.node].rev() for s in repo.mq.applied)
1485 1471 if set(destmap.values()) & mqapplied:
1486 1472 raise error.Abort(_('cannot rebase onto an applied mq patch'))
1487 1473
1488 1474 # Get "cycle" error early by exhausting the generator.
1489 1475 sortedsrc = list(sortsource(destmap)) # a list of sorted revs
1490 1476 if not sortedsrc:
1491 1477 raise error.Abort(_('no matching revisions'))
1492 1478
1493 1479 # Only check the first batch of revisions to rebase not depending on other
1494 1480 # rebaseset. This means "source is ancestor of destination" for the second
1495 1481 # (and following) batches of revisions are not checked here. We rely on
1496 1482 # "defineparents" to do that check.
1497 1483 roots = list(repo.set('roots(%ld)', sortedsrc[0]))
1498 1484 if not roots:
1499 1485 raise error.Abort(_('no matching revisions'))
1500 1486 roots.sort()
1501 1487 state = dict.fromkeys(rebaseset, revtodo)
1502 1488 emptyrebase = (len(sortedsrc) == 1)
1503 1489 for root in roots:
1504 1490 dest = repo[destmap[root.rev()]]
1505 1491 commonbase = root.ancestor(dest)
1506 1492 if commonbase == root:
1507 1493 raise error.Abort(_('source is ancestor of destination'))
1508 1494 if commonbase == dest:
1509 1495 wctx = repo[None]
1510 1496 if dest == wctx.p1():
1511 1497 # when rebasing to '.', it will use the current wd branch name
1512 1498 samebranch = root.branch() == wctx.branch()
1513 1499 else:
1514 1500 samebranch = root.branch() == dest.branch()
1515 1501 if not collapse and samebranch and dest in root.parents():
1516 1502 # mark the revision as done by setting its new revision
1517 1503 # equal to its old (current) revisions
1518 1504 state[root.rev()] = root.rev()
1519 1505 repo.ui.debug('source is a child of destination\n')
1520 1506 continue
1521 1507
1522 1508 emptyrebase = False
1523 1509 repo.ui.debug('rebase onto %s starting from %s\n' % (dest, root))
1524 1510 if emptyrebase:
1525 1511 return None
1526 1512 for rev in sorted(state):
1527 1513 parents = [p for p in repo.changelog.parentrevs(rev) if p != nullrev]
1528 1514 # if all parents of this revision are done, then so is this revision
1529 1515 if parents and all((state.get(p) == p for p in parents)):
1530 1516 state[rev] = rev
1531 1517 return originalwd, destmap, state
1532 1518
1533 1519 def clearrebased(ui, repo, destmap, state, skipped, collapsedas=None,
1534 1520 keepf=False):
1535 1521 """dispose of rebased revision at the end of the rebase
1536 1522
1537 1523 If `collapsedas` is not None, the rebase was a collapse whose result if the
1538 1524 `collapsedas` node.
1539 1525
1540 1526 If `keepf` is not True, the rebase has --keep set and no nodes should be
1541 1527 removed (but bookmarks still need to be moved).
1542 1528 """
1543 1529 tonode = repo.changelog.node
1544 1530 replacements = {}
1545 1531 moves = {}
1546 1532 for rev, newrev in sorted(state.items()):
1547 1533 if newrev >= 0 and newrev != rev:
1548 1534 oldnode = tonode(rev)
1549 1535 newnode = collapsedas or tonode(newrev)
1550 1536 moves[oldnode] = newnode
1551 1537 if not keepf:
1552 1538 if rev in skipped:
1553 1539 succs = ()
1554 1540 else:
1555 1541 succs = (newnode,)
1556 1542 replacements[oldnode] = succs
1557 1543 scmutil.cleanupnodes(repo, replacements, 'rebase', moves)
1558 1544
1559 1545 def pullrebase(orig, ui, repo, *args, **opts):
1560 1546 'Call rebase after pull if the latter has been invoked with --rebase'
1561 1547 ret = None
1562 1548 if opts.get('rebase'):
1563 1549 if ui.configbool('commands', 'rebase.requiredest'):
1564 1550 msg = _('rebase destination required by configuration')
1565 1551 hint = _('use hg pull followed by hg rebase -d DEST')
1566 1552 raise error.Abort(msg, hint=hint)
1567 1553
1568 1554 with repo.wlock(), repo.lock():
1569 1555 if opts.get('update'):
1570 1556 del opts['update']
1571 1557 ui.debug('--update and --rebase are not compatible, ignoring '
1572 1558 'the update flag\n')
1573 1559
1574 1560 cmdutil.checkunfinished(repo)
1575 1561 cmdutil.bailifchanged(repo, hint=_('cannot pull with rebase: '
1576 1562 'please commit or shelve your changes first'))
1577 1563
1578 1564 revsprepull = len(repo)
1579 1565 origpostincoming = commands.postincoming
1580 1566 def _dummy(*args, **kwargs):
1581 1567 pass
1582 1568 commands.postincoming = _dummy
1583 1569 try:
1584 1570 ret = orig(ui, repo, *args, **opts)
1585 1571 finally:
1586 1572 commands.postincoming = origpostincoming
1587 1573 revspostpull = len(repo)
1588 1574 if revspostpull > revsprepull:
1589 1575 # --rev option from pull conflict with rebase own --rev
1590 1576 # dropping it
1591 1577 if 'rev' in opts:
1592 1578 del opts['rev']
1593 1579 # positional argument from pull conflicts with rebase's own
1594 1580 # --source.
1595 1581 if 'source' in opts:
1596 1582 del opts['source']
1597 1583 # revsprepull is the len of the repo, not revnum of tip.
1598 1584 destspace = list(repo.changelog.revs(start=revsprepull))
1599 1585 opts['_destspace'] = destspace
1600 1586 try:
1601 1587 rebase(ui, repo, **opts)
1602 1588 except error.NoMergeDestAbort:
1603 1589 # we can maybe update instead
1604 1590 rev, _a, _b = destutil.destupdate(repo)
1605 1591 if rev == repo['.'].rev():
1606 1592 ui.status(_('nothing to rebase\n'))
1607 1593 else:
1608 1594 ui.status(_('nothing to rebase - updating instead\n'))
1609 1595 # not passing argument to get the bare update behavior
1610 1596 # with warning and trumpets
1611 1597 commands.update(ui, repo)
1612 1598 else:
1613 1599 if opts.get('tool'):
1614 1600 raise error.Abort(_('--tool can only be used with --rebase'))
1615 1601 ret = orig(ui, repo, *args, **opts)
1616 1602
1617 1603 return ret
1618 1604
1619 1605 def _filterobsoleterevs(repo, revs):
1620 1606 """returns a set of the obsolete revisions in revs"""
1621 1607 return set(r for r in revs if repo[r].obsolete())
1622 1608
1623 1609 def _computeobsoletenotrebased(repo, rebaseobsrevs, destmap):
1624 1610 """return a mapping obsolete => successor for all obsolete nodes to be
1625 1611 rebased that have a successors in the destination
1626 1612
1627 1613 obsolete => None entries in the mapping indicate nodes with no successor"""
1628 1614 obsoletenotrebased = {}
1629 1615
1630 1616 assert repo.filtername is None
1631 1617 cl = repo.changelog
1632 1618 nodemap = cl.nodemap
1633 1619 for srcrev in rebaseobsrevs:
1634 1620 srcnode = cl.node(srcrev)
1635 1621 destnode = cl.node(destmap[srcrev])
1636 1622 # XXX: more advanced APIs are required to handle split correctly
1637 1623 successors = list(obsutil.allsuccessors(repo.obsstore, [srcnode]))
1638 1624 if len(successors) == 1:
1639 1625 # obsutil.allsuccessors includes node itself. When the list only
1640 1626 # contains one element, it means there are no successors.
1641 1627 obsoletenotrebased[srcrev] = None
1642 1628 else:
1643 1629 for succnode in successors:
1644 1630 if succnode == srcnode or succnode not in nodemap:
1645 1631 continue
1646 1632 if cl.isancestor(succnode, destnode):
1647 1633 obsoletenotrebased[srcrev] = nodemap[succnode]
1648 1634 break
1649 1635
1650 1636 return obsoletenotrebased
1651 1637
1652 1638 def summaryhook(ui, repo):
1653 1639 if not repo.vfs.exists('rebasestate'):
1654 1640 return
1655 1641 try:
1656 1642 rbsrt = rebaseruntime(repo, ui, {})
1657 1643 rbsrt.restorestatus()
1658 1644 state = rbsrt.state
1659 1645 except error.RepoLookupError:
1660 1646 # i18n: column positioning for "hg summary"
1661 1647 msg = _('rebase: (use "hg rebase --abort" to clear broken state)\n')
1662 1648 ui.write(msg)
1663 1649 return
1664 1650 numrebased = len([i for i in state.itervalues() if i >= 0])
1665 1651 # i18n: column positioning for "hg summary"
1666 1652 ui.write(_('rebase: %s, %s (rebase --continue)\n') %
1667 1653 (ui.label(_('%d rebased'), 'rebase.rebased') % numrebased,
1668 1654 ui.label(_('%d remaining'), 'rebase.remaining') %
1669 1655 (len(state) - numrebased)))
1670 1656
1671 1657 def uisetup(ui):
1672 1658 #Replace pull with a decorator to provide --rebase option
1673 1659 entry = extensions.wrapcommand(commands.table, 'pull', pullrebase)
1674 1660 entry[1].append(('', 'rebase', None,
1675 1661 _("rebase working directory to branch head")))
1676 1662 entry[1].append(('t', 'tool', '',
1677 1663 _("specify merge tool for rebase")))
1678 1664 cmdutil.summaryhooks.add('rebase', summaryhook)
1679 1665 cmdutil.unfinishedstates.append(
1680 1666 ['rebasestate', False, False, _('rebase in progress'),
1681 1667 _("use 'hg rebase --continue' or 'hg rebase --abort'")])
1682 1668 cmdutil.afterresolvedstates.append(
1683 1669 ['rebasestate', _('hg rebase --continue')])
@@ -1,1087 +1,1100 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 def loadconfigtable(ui, extname, configtable):
19 19 """update config item known to the ui with the extension ones"""
20 20 for section, items in configtable.items():
21 21 knownitems = ui._knownconfig.setdefault(section, itemregister())
22 22 knownkeys = set(knownitems)
23 23 newkeys = set(items)
24 24 for key in sorted(knownkeys & newkeys):
25 25 msg = "extension '%s' overwrite config item '%s.%s'"
26 26 msg %= (extname, section, key)
27 27 ui.develwarn(msg, config='warn-config')
28 28
29 29 knownitems.update(items)
30 30
31 31 class configitem(object):
32 32 """represent a known config item
33 33
34 34 :section: the official config section where to find this item,
35 35 :name: the official name within the section,
36 36 :default: default value for this item,
37 37 :alias: optional list of tuples as alternatives,
38 38 :generic: this is a generic definition, match name using regular expression.
39 39 """
40 40
41 41 def __init__(self, section, name, default=None, alias=(),
42 42 generic=False, priority=0):
43 43 self.section = section
44 44 self.name = name
45 45 self.default = default
46 46 self.alias = list(alias)
47 47 self.generic = generic
48 48 self.priority = priority
49 49 self._re = None
50 50 if generic:
51 51 self._re = re.compile(self.name)
52 52
53 53 class itemregister(dict):
54 54 """A specialized dictionary that can handle wild-card selection"""
55 55
56 56 def __init__(self):
57 57 super(itemregister, self).__init__()
58 58 self._generics = set()
59 59
60 60 def update(self, other):
61 61 super(itemregister, self).update(other)
62 62 self._generics.update(other._generics)
63 63
64 64 def __setitem__(self, key, item):
65 65 super(itemregister, self).__setitem__(key, item)
66 66 if item.generic:
67 67 self._generics.add(item)
68 68
69 69 def get(self, key):
70 70 if key in self:
71 71 return self[key]
72 72
73 73 # search for a matching generic item
74 74 generics = sorted(self._generics, key=(lambda x: (x.priority, x.name)))
75 75 for item in generics:
76 76 if item._re.match(key):
77 77 return item
78 78
79 79 # fallback to dict get
80 80 return super(itemregister, self).get(key)
81 81
82 82 coreitems = {}
83 83
84 84 def _register(configtable, *args, **kwargs):
85 85 item = configitem(*args, **kwargs)
86 86 section = configtable.setdefault(item.section, itemregister())
87 87 if item.name in section:
88 88 msg = "duplicated config item registration for '%s.%s'"
89 89 raise error.ProgrammingError(msg % (item.section, item.name))
90 90 section[item.name] = item
91 91
92 92 # special value for case where the default is derived from other values
93 93 dynamicdefault = object()
94 94
95 95 # Registering actual config items
96 96
97 97 def getitemregister(configtable):
98 98 return functools.partial(_register, configtable)
99 99
100 100 coreconfigitem = getitemregister(coreitems)
101 101
102 102 coreconfigitem('alias', '.*',
103 103 default=None,
104 104 generic=True,
105 105 )
106 106 coreconfigitem('annotate', 'nodates',
107 107 default=False,
108 108 )
109 109 coreconfigitem('annotate', 'showfunc',
110 110 default=False,
111 111 )
112 112 coreconfigitem('annotate', 'unified',
113 113 default=None,
114 114 )
115 115 coreconfigitem('annotate', 'git',
116 116 default=False,
117 117 )
118 118 coreconfigitem('annotate', 'ignorews',
119 119 default=False,
120 120 )
121 121 coreconfigitem('annotate', 'ignorewsamount',
122 122 default=False,
123 123 )
124 124 coreconfigitem('annotate', 'ignoreblanklines',
125 125 default=False,
126 126 )
127 127 coreconfigitem('annotate', 'ignorewseol',
128 128 default=False,
129 129 )
130 130 coreconfigitem('annotate', 'nobinary',
131 131 default=False,
132 132 )
133 133 coreconfigitem('annotate', 'noprefix',
134 134 default=False,
135 135 )
136 136 coreconfigitem('auth', 'cookiefile',
137 137 default=None,
138 138 )
139 139 # bookmarks.pushing: internal hack for discovery
140 140 coreconfigitem('bookmarks', 'pushing',
141 141 default=list,
142 142 )
143 143 # bundle.mainreporoot: internal hack for bundlerepo
144 144 coreconfigitem('bundle', 'mainreporoot',
145 145 default='',
146 146 )
147 147 # bundle.reorder: experimental config
148 148 coreconfigitem('bundle', 'reorder',
149 149 default='auto',
150 150 )
151 151 coreconfigitem('censor', 'policy',
152 152 default='abort',
153 153 )
154 154 coreconfigitem('chgserver', 'idletimeout',
155 155 default=3600,
156 156 )
157 157 coreconfigitem('chgserver', 'skiphash',
158 158 default=False,
159 159 )
160 160 coreconfigitem('cmdserver', 'log',
161 161 default=None,
162 162 )
163 163 coreconfigitem('color', '.*',
164 164 default=None,
165 165 generic=True,
166 166 )
167 167 coreconfigitem('color', 'mode',
168 168 default='auto',
169 169 )
170 170 coreconfigitem('color', 'pagermode',
171 171 default=dynamicdefault,
172 172 )
173 173 coreconfigitem('commands', 'status.relative',
174 174 default=False,
175 175 )
176 176 coreconfigitem('commands', 'status.skipstates',
177 177 default=[],
178 178 )
179 179 coreconfigitem('commands', 'status.verbose',
180 180 default=False,
181 181 )
182 182 coreconfigitem('commands', 'update.check',
183 183 default=None,
184 184 # Deprecated, remove after 4.4 release
185 185 alias=[('experimental', 'updatecheck')]
186 186 )
187 187 coreconfigitem('commands', 'update.requiredest',
188 188 default=False,
189 189 )
190 190 coreconfigitem('committemplate', '.*',
191 191 default=None,
192 192 generic=True,
193 193 )
194 194 coreconfigitem('debug', 'dirstate.delaywrite',
195 195 default=0,
196 196 )
197 197 coreconfigitem('defaults', '.*',
198 198 default=None,
199 199 generic=True,
200 200 )
201 201 coreconfigitem('devel', 'all-warnings',
202 202 default=False,
203 203 )
204 204 coreconfigitem('devel', 'bundle2.debug',
205 205 default=False,
206 206 )
207 207 coreconfigitem('devel', 'cache-vfs',
208 208 default=None,
209 209 )
210 210 coreconfigitem('devel', 'check-locks',
211 211 default=False,
212 212 )
213 213 coreconfigitem('devel', 'check-relroot',
214 214 default=False,
215 215 )
216 216 coreconfigitem('devel', 'default-date',
217 217 default=None,
218 218 )
219 219 coreconfigitem('devel', 'deprec-warn',
220 220 default=False,
221 221 )
222 222 coreconfigitem('devel', 'disableloaddefaultcerts',
223 223 default=False,
224 224 )
225 225 coreconfigitem('devel', 'warn-empty-changegroup',
226 226 default=False,
227 227 )
228 228 coreconfigitem('devel', 'legacy.exchange',
229 229 default=list,
230 230 )
231 231 coreconfigitem('devel', 'servercafile',
232 232 default='',
233 233 )
234 234 coreconfigitem('devel', 'serverexactprotocol',
235 235 default='',
236 236 )
237 237 coreconfigitem('devel', 'serverrequirecert',
238 238 default=False,
239 239 )
240 240 coreconfigitem('devel', 'strip-obsmarkers',
241 241 default=True,
242 242 )
243 243 coreconfigitem('devel', 'warn-config',
244 244 default=None,
245 245 )
246 246 coreconfigitem('devel', 'warn-config-default',
247 247 default=None,
248 248 )
249 249 coreconfigitem('devel', 'user.obsmarker',
250 250 default=None,
251 251 )
252 252 coreconfigitem('diff', 'nodates',
253 253 default=False,
254 254 )
255 255 coreconfigitem('diff', 'showfunc',
256 256 default=False,
257 257 )
258 258 coreconfigitem('diff', 'unified',
259 259 default=None,
260 260 )
261 261 coreconfigitem('diff', 'git',
262 262 default=False,
263 263 )
264 264 coreconfigitem('diff', 'ignorews',
265 265 default=False,
266 266 )
267 267 coreconfigitem('diff', 'ignorewsamount',
268 268 default=False,
269 269 )
270 270 coreconfigitem('diff', 'ignoreblanklines',
271 271 default=False,
272 272 )
273 273 coreconfigitem('diff', 'ignorewseol',
274 274 default=False,
275 275 )
276 276 coreconfigitem('diff', 'nobinary',
277 277 default=False,
278 278 )
279 279 coreconfigitem('diff', 'noprefix',
280 280 default=False,
281 281 )
282 282 coreconfigitem('email', 'bcc',
283 283 default=None,
284 284 )
285 285 coreconfigitem('email', 'cc',
286 286 default=None,
287 287 )
288 288 coreconfigitem('email', 'charsets',
289 289 default=list,
290 290 )
291 291 coreconfigitem('email', 'from',
292 292 default=None,
293 293 )
294 294 coreconfigitem('email', 'method',
295 295 default='smtp',
296 296 )
297 297 coreconfigitem('email', 'reply-to',
298 298 default=None,
299 299 )
300 300 coreconfigitem('experimental', 'allowdivergence',
301 301 default=False,
302 302 )
303 303 coreconfigitem('experimental', 'archivemetatemplate',
304 304 default=dynamicdefault,
305 305 )
306 306 coreconfigitem('experimental', 'bundle-phases',
307 307 default=False,
308 308 )
309 309 coreconfigitem('experimental', 'bundle2-advertise',
310 310 default=True,
311 311 )
312 312 coreconfigitem('experimental', 'bundle2-output-capture',
313 313 default=False,
314 314 )
315 315 coreconfigitem('experimental', 'bundle2.pushback',
316 316 default=False,
317 317 )
318 318 coreconfigitem('experimental', 'bundle2lazylocking',
319 319 default=False,
320 320 )
321 321 coreconfigitem('experimental', 'bundlecomplevel',
322 322 default=None,
323 323 )
324 324 coreconfigitem('experimental', 'changegroup3',
325 325 default=False,
326 326 )
327 327 coreconfigitem('experimental', 'clientcompressionengines',
328 328 default=list,
329 329 )
330 330 coreconfigitem('experimental', 'copytrace',
331 331 default='on',
332 332 )
333 333 coreconfigitem('experimental', 'copytrace.sourcecommitlimit',
334 334 default=100,
335 335 )
336 336 coreconfigitem('experimental', 'crecordtest',
337 337 default=None,
338 338 )
339 339 coreconfigitem('experimental', 'editortmpinhg',
340 340 default=False,
341 341 )
342 342 coreconfigitem('experimental', 'maxdeltachainspan',
343 343 default=-1,
344 344 )
345 345 coreconfigitem('experimental', 'mmapindexthreshold',
346 346 default=None,
347 347 )
348 348 coreconfigitem('experimental', 'nonnormalparanoidcheck',
349 349 default=False,
350 350 )
351 351 coreconfigitem('experimental', 'effect-flags',
352 352 default=False,
353 353 )
354 354 coreconfigitem('experimental', 'stabilization',
355 355 default=list,
356 356 alias=[('experimental', 'evolution')],
357 357 )
358 358 coreconfigitem('experimental', 'stabilization.bundle-obsmarker',
359 359 default=False,
360 360 alias=[('experimental', 'evolution.bundle-obsmarker')],
361 361 )
362 362 coreconfigitem('experimental', 'stabilization.track-operation',
363 363 default=True,
364 364 alias=[('experimental', 'evolution.track-operation')]
365 365 )
366 366 coreconfigitem('experimental', 'exportableenviron',
367 367 default=list,
368 368 )
369 369 coreconfigitem('experimental', 'extendedheader.index',
370 370 default=None,
371 371 )
372 372 coreconfigitem('experimental', 'extendedheader.similarity',
373 373 default=False,
374 374 )
375 375 coreconfigitem('experimental', 'format.compression',
376 376 default='zlib',
377 377 )
378 378 coreconfigitem('experimental', 'graphshorten',
379 379 default=False,
380 380 )
381 381 coreconfigitem('experimental', 'graphstyle.parent',
382 382 default=dynamicdefault,
383 383 )
384 384 coreconfigitem('experimental', 'graphstyle.missing',
385 385 default=dynamicdefault,
386 386 )
387 387 coreconfigitem('experimental', 'graphstyle.grandparent',
388 388 default=dynamicdefault,
389 389 )
390 390 coreconfigitem('experimental', 'hook-track-tags',
391 391 default=False,
392 392 )
393 393 coreconfigitem('experimental', 'httppostargs',
394 394 default=False,
395 395 )
396 396 coreconfigitem('experimental', 'manifestv2',
397 397 default=False,
398 398 )
399 399 coreconfigitem('experimental', 'mergedriver',
400 400 default=None,
401 401 )
402 402 coreconfigitem('experimental', 'obsmarkers-exchange-debug',
403 403 default=False,
404 404 )
405 405 coreconfigitem('experimental', 'rebase.multidest',
406 406 default=False,
407 407 )
408 408 coreconfigitem('experimental', 'revertalternateinteractivemode',
409 409 default=True,
410 410 )
411 411 coreconfigitem('experimental', 'revlogv2',
412 412 default=None,
413 413 )
414 414 coreconfigitem('experimental', 'spacemovesdown',
415 415 default=False,
416 416 )
417 417 coreconfigitem('experimental', 'sparse-read',
418 418 default=False,
419 419 )
420 420 coreconfigitem('experimental', 'sparse-read.density-threshold',
421 421 default=0.25,
422 422 )
423 423 coreconfigitem('experimental', 'sparse-read.min-block-size',
424 424 default='256K',
425 425 )
426 426 coreconfigitem('experimental', 'treemanifest',
427 427 default=False,
428 428 )
429 429 coreconfigitem('extensions', '.*',
430 430 default=None,
431 431 generic=True,
432 432 )
433 433 coreconfigitem('extdata', '.*',
434 434 default=None,
435 435 generic=True,
436 436 )
437 437 coreconfigitem('format', 'aggressivemergedeltas',
438 438 default=False,
439 439 )
440 440 coreconfigitem('format', 'chunkcachesize',
441 441 default=None,
442 442 )
443 443 coreconfigitem('format', 'dotencode',
444 444 default=True,
445 445 )
446 446 coreconfigitem('format', 'generaldelta',
447 447 default=False,
448 448 )
449 449 coreconfigitem('format', 'manifestcachesize',
450 450 default=None,
451 451 )
452 452 coreconfigitem('format', 'maxchainlen',
453 453 default=None,
454 454 )
455 455 coreconfigitem('format', 'obsstore-version',
456 456 default=None,
457 457 )
458 458 coreconfigitem('format', 'usefncache',
459 459 default=True,
460 460 )
461 461 coreconfigitem('format', 'usegeneraldelta',
462 462 default=True,
463 463 )
464 464 coreconfigitem('format', 'usestore',
465 465 default=True,
466 466 )
467 467 coreconfigitem('hooks', '.*',
468 468 default=dynamicdefault,
469 469 generic=True,
470 470 )
471 471 coreconfigitem('hgweb-paths', '.*',
472 472 default=list,
473 473 generic=True,
474 474 )
475 475 coreconfigitem('hostfingerprints', '.*',
476 476 default=list,
477 477 generic=True,
478 478 )
479 479 coreconfigitem('hostsecurity', 'ciphers',
480 480 default=None,
481 481 )
482 482 coreconfigitem('hostsecurity', 'disabletls10warning',
483 483 default=False,
484 484 )
485 485 coreconfigitem('hostsecurity', 'minimumprotocol',
486 486 default=dynamicdefault,
487 487 )
488 488 coreconfigitem('hostsecurity', '.*:minimumprotocol$',
489 489 default=dynamicdefault,
490 490 generic=True,
491 491 )
492 492 coreconfigitem('hostsecurity', '.*:ciphers$',
493 493 default=dynamicdefault,
494 494 generic=True,
495 495 )
496 496 coreconfigitem('hostsecurity', '.*:fingerprints$',
497 497 default=list,
498 498 generic=True,
499 499 )
500 500 coreconfigitem('hostsecurity', '.*:verifycertsfile$',
501 501 default=None,
502 502 generic=True,
503 503 )
504 504
505 505 coreconfigitem('http_proxy', 'always',
506 506 default=False,
507 507 )
508 508 coreconfigitem('http_proxy', 'host',
509 509 default=None,
510 510 )
511 511 coreconfigitem('http_proxy', 'no',
512 512 default=list,
513 513 )
514 514 coreconfigitem('http_proxy', 'passwd',
515 515 default=None,
516 516 )
517 517 coreconfigitem('http_proxy', 'user',
518 518 default=None,
519 519 )
520 520 coreconfigitem('logtoprocess', 'commandexception',
521 521 default=None,
522 522 )
523 523 coreconfigitem('logtoprocess', 'commandfinish',
524 524 default=None,
525 525 )
526 526 coreconfigitem('logtoprocess', 'command',
527 527 default=None,
528 528 )
529 529 coreconfigitem('logtoprocess', 'develwarn',
530 530 default=None,
531 531 )
532 532 coreconfigitem('logtoprocess', 'uiblocked',
533 533 default=None,
534 534 )
535 535 coreconfigitem('merge', 'checkunknown',
536 536 default='abort',
537 537 )
538 538 coreconfigitem('merge', 'checkignored',
539 539 default='abort',
540 540 )
541 541 coreconfigitem('merge', 'followcopies',
542 542 default=True,
543 543 )
544 544 coreconfigitem('merge', 'on-failure',
545 545 default='continue',
546 546 )
547 547 coreconfigitem('merge', 'preferancestor',
548 548 default=lambda: ['*'],
549 549 )
550 550 coreconfigitem('merge-tools', '.*',
551 551 default=None,
552 552 generic=True,
553 553 )
554 554 coreconfigitem('merge-tools', r'.*\.args$',
555 555 default="$local $base $other",
556 556 generic=True,
557 557 priority=-1,
558 558 )
559 559 coreconfigitem('merge-tools', r'.*\.binary$',
560 560 default=False,
561 561 generic=True,
562 562 priority=-1,
563 563 )
564 564 coreconfigitem('merge-tools', r'.*\.check$',
565 565 default=list,
566 566 generic=True,
567 567 priority=-1,
568 568 )
569 569 coreconfigitem('merge-tools', r'.*\.checkchanged$',
570 570 default=False,
571 571 generic=True,
572 572 priority=-1,
573 573 )
574 574 coreconfigitem('merge-tools', r'.*\.executable$',
575 575 default=dynamicdefault,
576 576 generic=True,
577 577 priority=-1,
578 578 )
579 579 coreconfigitem('merge-tools', r'.*\.fixeol$',
580 580 default=False,
581 581 generic=True,
582 582 priority=-1,
583 583 )
584 584 coreconfigitem('merge-tools', r'.*\.gui$',
585 585 default=False,
586 586 generic=True,
587 587 priority=-1,
588 588 )
589 589 coreconfigitem('merge-tools', r'.*\.priority$',
590 590 default=0,
591 591 generic=True,
592 592 priority=-1,
593 593 )
594 594 coreconfigitem('merge-tools', r'.*\.premerge$',
595 595 default=dynamicdefault,
596 596 generic=True,
597 597 priority=-1,
598 598 )
599 599 coreconfigitem('merge-tools', r'.*\.symlink$',
600 600 default=False,
601 601 generic=True,
602 602 priority=-1,
603 603 )
604 604 coreconfigitem('pager', 'attend-.*',
605 605 default=dynamicdefault,
606 606 generic=True,
607 607 )
608 608 coreconfigitem('pager', 'ignore',
609 609 default=list,
610 610 )
611 611 coreconfigitem('pager', 'pager',
612 612 default=dynamicdefault,
613 613 )
614 614 coreconfigitem('patch', 'eol',
615 615 default='strict',
616 616 )
617 617 coreconfigitem('patch', 'fuzz',
618 618 default=2,
619 619 )
620 620 coreconfigitem('paths', 'default',
621 621 default=None,
622 622 )
623 623 coreconfigitem('paths', 'default-push',
624 624 default=None,
625 625 )
626 626 coreconfigitem('paths', '.*',
627 627 default=None,
628 628 generic=True,
629 629 )
630 630 coreconfigitem('phases', 'checksubrepos',
631 631 default='follow',
632 632 )
633 633 coreconfigitem('phases', 'new-commit',
634 634 default='draft',
635 635 )
636 636 coreconfigitem('phases', 'publish',
637 637 default=True,
638 638 )
639 639 coreconfigitem('profiling', 'enabled',
640 640 default=False,
641 641 )
642 642 coreconfigitem('profiling', 'format',
643 643 default='text',
644 644 )
645 645 coreconfigitem('profiling', 'freq',
646 646 default=1000,
647 647 )
648 648 coreconfigitem('profiling', 'limit',
649 649 default=30,
650 650 )
651 651 coreconfigitem('profiling', 'nested',
652 652 default=0,
653 653 )
654 654 coreconfigitem('profiling', 'output',
655 655 default=None,
656 656 )
657 657 coreconfigitem('profiling', 'showmax',
658 658 default=0.999,
659 659 )
660 660 coreconfigitem('profiling', 'showmin',
661 661 default=dynamicdefault,
662 662 )
663 663 coreconfigitem('profiling', 'sort',
664 664 default='inlinetime',
665 665 )
666 666 coreconfigitem('profiling', 'statformat',
667 667 default='hotpath',
668 668 )
669 669 coreconfigitem('profiling', 'type',
670 670 default='stat',
671 671 )
672 672 coreconfigitem('progress', 'assume-tty',
673 673 default=False,
674 674 )
675 675 coreconfigitem('progress', 'changedelay',
676 676 default=1,
677 677 )
678 678 coreconfigitem('progress', 'clear-complete',
679 679 default=True,
680 680 )
681 681 coreconfigitem('progress', 'debug',
682 682 default=False,
683 683 )
684 684 coreconfigitem('progress', 'delay',
685 685 default=3,
686 686 )
687 687 coreconfigitem('progress', 'disable',
688 688 default=False,
689 689 )
690 690 coreconfigitem('progress', 'estimateinterval',
691 691 default=60.0,
692 692 )
693 693 coreconfigitem('progress', 'format',
694 694 default=lambda: ['topic', 'bar', 'number', 'estimate'],
695 695 )
696 696 coreconfigitem('progress', 'refresh',
697 697 default=0.1,
698 698 )
699 699 coreconfigitem('progress', 'width',
700 700 default=dynamicdefault,
701 701 )
702 702 coreconfigitem('push', 'pushvars.server',
703 703 default=False,
704 704 )
705 705 coreconfigitem('server', 'bundle1',
706 706 default=True,
707 707 )
708 708 coreconfigitem('server', 'bundle1gd',
709 709 default=None,
710 710 )
711 711 coreconfigitem('server', 'bundle1.pull',
712 712 default=None,
713 713 )
714 714 coreconfigitem('server', 'bundle1gd.pull',
715 715 default=None,
716 716 )
717 717 coreconfigitem('server', 'bundle1.push',
718 718 default=None,
719 719 )
720 720 coreconfigitem('server', 'bundle1gd.push',
721 721 default=None,
722 722 )
723 723 coreconfigitem('server', 'compressionengines',
724 724 default=list,
725 725 )
726 726 coreconfigitem('server', 'concurrent-push-mode',
727 727 default='strict',
728 728 )
729 729 coreconfigitem('server', 'disablefullbundle',
730 730 default=False,
731 731 )
732 732 coreconfigitem('server', 'maxhttpheaderlen',
733 733 default=1024,
734 734 )
735 735 coreconfigitem('server', 'preferuncompressed',
736 736 default=False,
737 737 )
738 738 coreconfigitem('server', 'uncompressed',
739 739 default=True,
740 740 )
741 741 coreconfigitem('server', 'uncompressedallowsecret',
742 742 default=False,
743 743 )
744 744 coreconfigitem('server', 'validate',
745 745 default=False,
746 746 )
747 747 coreconfigitem('server', 'zliblevel',
748 748 default=-1,
749 749 )
750 750 coreconfigitem('smtp', 'host',
751 751 default=None,
752 752 )
753 753 coreconfigitem('smtp', 'local_hostname',
754 754 default=None,
755 755 )
756 756 coreconfigitem('smtp', 'password',
757 757 default=None,
758 758 )
759 759 coreconfigitem('smtp', 'port',
760 760 default=dynamicdefault,
761 761 )
762 762 coreconfigitem('smtp', 'tls',
763 763 default='none',
764 764 )
765 765 coreconfigitem('smtp', 'username',
766 766 default=None,
767 767 )
768 768 coreconfigitem('sparse', 'missingwarning',
769 769 default=True,
770 770 )
771 771 coreconfigitem('templates', '.*',
772 772 default=None,
773 773 generic=True,
774 774 )
775 775 coreconfigitem('trusted', 'groups',
776 776 default=list,
777 777 )
778 778 coreconfigitem('trusted', 'users',
779 779 default=list,
780 780 )
781 781 coreconfigitem('ui', '_usedassubrepo',
782 782 default=False,
783 783 )
784 784 coreconfigitem('ui', 'allowemptycommit',
785 785 default=False,
786 786 )
787 787 coreconfigitem('ui', 'archivemeta',
788 788 default=True,
789 789 )
790 790 coreconfigitem('ui', 'askusername',
791 791 default=False,
792 792 )
793 793 coreconfigitem('ui', 'clonebundlefallback',
794 794 default=False,
795 795 )
796 796 coreconfigitem('ui', 'clonebundleprefers',
797 797 default=list,
798 798 )
799 799 coreconfigitem('ui', 'clonebundles',
800 800 default=True,
801 801 )
802 802 coreconfigitem('ui', 'color',
803 803 default='auto',
804 804 )
805 805 coreconfigitem('ui', 'commitsubrepos',
806 806 default=False,
807 807 )
808 808 coreconfigitem('ui', 'debug',
809 809 default=False,
810 810 )
811 811 coreconfigitem('ui', 'debugger',
812 812 default=None,
813 813 )
814 814 coreconfigitem('ui', 'fallbackencoding',
815 815 default=None,
816 816 )
817 817 coreconfigitem('ui', 'forcecwd',
818 818 default=None,
819 819 )
820 820 coreconfigitem('ui', 'forcemerge',
821 821 default=None,
822 822 )
823 823 coreconfigitem('ui', 'formatdebug',
824 824 default=False,
825 825 )
826 826 coreconfigitem('ui', 'formatjson',
827 827 default=False,
828 828 )
829 829 coreconfigitem('ui', 'formatted',
830 830 default=None,
831 831 )
832 832 coreconfigitem('ui', 'graphnodetemplate',
833 833 default=None,
834 834 )
835 835 coreconfigitem('ui', 'http2debuglevel',
836 836 default=None,
837 837 )
838 838 coreconfigitem('ui', 'interactive',
839 839 default=None,
840 840 )
841 841 coreconfigitem('ui', 'interface',
842 842 default=None,
843 843 )
844 844 coreconfigitem('ui', 'interface.chunkselector',
845 845 default=None,
846 846 )
847 847 coreconfigitem('ui', 'logblockedtimes',
848 848 default=False,
849 849 )
850 850 coreconfigitem('ui', 'logtemplate',
851 851 default=None,
852 852 )
853 853 coreconfigitem('ui', 'merge',
854 854 default=None,
855 855 )
856 856 coreconfigitem('ui', 'mergemarkers',
857 857 default='basic',
858 858 )
859 859 coreconfigitem('ui', 'mergemarkertemplate',
860 860 default=('{node|short} '
861 861 '{ifeq(tags, "tip", "", '
862 862 'ifeq(tags, "", "", "{tags} "))}'
863 863 '{if(bookmarks, "{bookmarks} ")}'
864 864 '{ifeq(branch, "default", "", "{branch} ")}'
865 865 '- {author|user}: {desc|firstline}')
866 866 )
867 867 coreconfigitem('ui', 'nontty',
868 868 default=False,
869 869 )
870 870 coreconfigitem('ui', 'origbackuppath',
871 871 default=None,
872 872 )
873 873 coreconfigitem('ui', 'paginate',
874 874 default=True,
875 875 )
876 876 coreconfigitem('ui', 'patch',
877 877 default=None,
878 878 )
879 879 coreconfigitem('ui', 'portablefilenames',
880 880 default='warn',
881 881 )
882 882 coreconfigitem('ui', 'promptecho',
883 883 default=False,
884 884 )
885 885 coreconfigitem('ui', 'quiet',
886 886 default=False,
887 887 )
888 888 coreconfigitem('ui', 'quietbookmarkmove',
889 889 default=False,
890 890 )
891 891 coreconfigitem('ui', 'remotecmd',
892 892 default='hg',
893 893 )
894 894 coreconfigitem('ui', 'report_untrusted',
895 895 default=True,
896 896 )
897 897 coreconfigitem('ui', 'rollback',
898 898 default=True,
899 899 )
900 900 coreconfigitem('ui', 'slash',
901 901 default=False,
902 902 )
903 903 coreconfigitem('ui', 'ssh',
904 904 default='ssh',
905 905 )
906 906 coreconfigitem('ui', 'statuscopies',
907 907 default=False,
908 908 )
909 909 coreconfigitem('ui', 'strict',
910 910 default=False,
911 911 )
912 912 coreconfigitem('ui', 'style',
913 913 default='',
914 914 )
915 915 coreconfigitem('ui', 'supportcontact',
916 916 default=None,
917 917 )
918 918 coreconfigitem('ui', 'textwidth',
919 919 default=78,
920 920 )
921 921 coreconfigitem('ui', 'timeout',
922 922 default='600',
923 923 )
924 924 coreconfigitem('ui', 'traceback',
925 925 default=False,
926 926 )
927 927 coreconfigitem('ui', 'tweakdefaults',
928 928 default=False,
929 929 )
930 930 coreconfigitem('ui', 'usehttp2',
931 931 default=False,
932 932 )
933 933 coreconfigitem('ui', 'username',
934 934 alias=[('ui', 'user')]
935 935 )
936 936 coreconfigitem('ui', 'verbose',
937 937 default=False,
938 938 )
939 939 coreconfigitem('verify', 'skipflags',
940 940 default=None,
941 941 )
942 942 coreconfigitem('web', 'allowbz2',
943 943 default=False,
944 944 )
945 945 coreconfigitem('web', 'allowgz',
946 946 default=False,
947 947 )
948 948 coreconfigitem('web', 'allowpull',
949 949 default=True,
950 950 )
951 951 coreconfigitem('web', 'allow_push',
952 952 default=list,
953 953 )
954 954 coreconfigitem('web', 'allowzip',
955 955 default=False,
956 956 )
957 957 coreconfigitem('web', 'archivesubrepos',
958 958 default=False,
959 959 )
960 960 coreconfigitem('web', 'cache',
961 961 default=True,
962 962 )
963 963 coreconfigitem('web', 'contact',
964 964 default=None,
965 965 )
966 966 coreconfigitem('web', 'deny_push',
967 967 default=list,
968 968 )
969 969 coreconfigitem('web', 'guessmime',
970 970 default=False,
971 971 )
972 972 coreconfigitem('web', 'hidden',
973 973 default=False,
974 974 )
975 975 coreconfigitem('web', 'labels',
976 976 default=list,
977 977 )
978 978 coreconfigitem('web', 'logoimg',
979 979 default='hglogo.png',
980 980 )
981 981 coreconfigitem('web', 'logourl',
982 982 default='https://mercurial-scm.org/',
983 983 )
984 984 coreconfigitem('web', 'accesslog',
985 985 default='-',
986 986 )
987 987 coreconfigitem('web', 'address',
988 988 default='',
989 989 )
990 990 coreconfigitem('web', 'allow_archive',
991 991 default=list,
992 992 )
993 993 coreconfigitem('web', 'allow_read',
994 994 default=list,
995 995 )
996 996 coreconfigitem('web', 'baseurl',
997 997 default=None,
998 998 )
999 999 coreconfigitem('web', 'cacerts',
1000 1000 default=None,
1001 1001 )
1002 1002 coreconfigitem('web', 'certificate',
1003 1003 default=None,
1004 1004 )
1005 1005 coreconfigitem('web', 'collapse',
1006 1006 default=False,
1007 1007 )
1008 1008 coreconfigitem('web', 'csp',
1009 1009 default=None,
1010 1010 )
1011 1011 coreconfigitem('web', 'deny_read',
1012 1012 default=list,
1013 1013 )
1014 1014 coreconfigitem('web', 'descend',
1015 1015 default=True,
1016 1016 )
1017 1017 coreconfigitem('web', 'description',
1018 1018 default="",
1019 1019 )
1020 1020 coreconfigitem('web', 'encoding',
1021 1021 default=lambda: encoding.encoding,
1022 1022 )
1023 1023 coreconfigitem('web', 'errorlog',
1024 1024 default='-',
1025 1025 )
1026 1026 coreconfigitem('web', 'ipv6',
1027 1027 default=False,
1028 1028 )
1029 1029 coreconfigitem('web', 'maxchanges',
1030 1030 default=10,
1031 1031 )
1032 1032 coreconfigitem('web', 'maxfiles',
1033 1033 default=10,
1034 1034 )
1035 1035 coreconfigitem('web', 'maxshortchanges',
1036 1036 default=60,
1037 1037 )
1038 1038 coreconfigitem('web', 'motd',
1039 1039 default='',
1040 1040 )
1041 1041 coreconfigitem('web', 'name',
1042 1042 default=dynamicdefault,
1043 1043 )
1044 1044 coreconfigitem('web', 'port',
1045 1045 default=8000,
1046 1046 )
1047 1047 coreconfigitem('web', 'prefix',
1048 1048 default='',
1049 1049 )
1050 1050 coreconfigitem('web', 'push_ssl',
1051 1051 default=True,
1052 1052 )
1053 1053 coreconfigitem('web', 'refreshinterval',
1054 1054 default=20,
1055 1055 )
1056 1056 coreconfigitem('web', 'staticurl',
1057 1057 default=None,
1058 1058 )
1059 1059 coreconfigitem('web', 'stripes',
1060 1060 default=1,
1061 1061 )
1062 1062 coreconfigitem('web', 'style',
1063 1063 default='paper',
1064 1064 )
1065 1065 coreconfigitem('web', 'templates',
1066 1066 default=None,
1067 1067 )
1068 1068 coreconfigitem('web', 'view',
1069 1069 default='served',
1070 1070 )
1071 1071 coreconfigitem('worker', 'backgroundclose',
1072 1072 default=dynamicdefault,
1073 1073 )
1074 1074 # Windows defaults to a limit of 512 open files. A buffer of 128
1075 1075 # should give us enough headway.
1076 1076 coreconfigitem('worker', 'backgroundclosemaxqueue',
1077 1077 default=384,
1078 1078 )
1079 1079 coreconfigitem('worker', 'backgroundcloseminfilecount',
1080 1080 default=2048,
1081 1081 )
1082 1082 coreconfigitem('worker', 'backgroundclosethreadcount',
1083 1083 default=4,
1084 1084 )
1085 1085 coreconfigitem('worker', 'numcpus',
1086 1086 default=None,
1087 1087 )
1088
1089 # Rebase related configuration moved to core because other extension are doing
1090 # strange things. For example, shelve import the extensions to reuse some bit
1091 # without formally loading it.
1092 coreconfigitem('commands', 'rebase.requiredest',
1093 default=False,
1094 )
1095 coreconfigitem('experimental', 'rebaseskipobsolete',
1096 default=True,
1097 )
1098 coreconfigitem('rebase', 'singletransaction',
1099 default=False,
1100 )
General Comments 0
You need to be logged in to leave comments. Login now