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