##// END OF EJS Templates
rebase: allow in-memory merge of the working copy parent...
Martin von Zweigbergk -
r36993:795eb53f default
parent child Browse files
Show More
@@ -1,1867 +1,1856 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, inmemory=False, opts=None):
139 def __init__(self, repo, ui, inmemory=False, 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 self.obsoletenotrebased = {}
177 self.obsoletenotrebased = {}
178 self.obsoletewithoutsuccessorindestination = set()
178 self.obsoletewithoutsuccessorindestination = set()
179 self.inmemory = inmemory
179 self.inmemory = inmemory
180
180
181 @property
181 @property
182 def repo(self):
182 def repo(self):
183 if self.prepared:
183 if self.prepared:
184 return self._repo.unfiltered()
184 return self._repo.unfiltered()
185 else:
185 else:
186 return self._repo
186 return self._repo
187
187
188 def storestatus(self, tr=None):
188 def storestatus(self, tr=None):
189 """Store the current status to allow recovery"""
189 """Store the current status to allow recovery"""
190 if tr:
190 if tr:
191 tr.addfilegenerator('rebasestate', ('rebasestate',),
191 tr.addfilegenerator('rebasestate', ('rebasestate',),
192 self._writestatus, location='plain')
192 self._writestatus, location='plain')
193 else:
193 else:
194 with self.repo.vfs("rebasestate", "w") as f:
194 with self.repo.vfs("rebasestate", "w") as f:
195 self._writestatus(f)
195 self._writestatus(f)
196
196
197 def _writestatus(self, f):
197 def _writestatus(self, f):
198 repo = self.repo
198 repo = self.repo
199 assert repo.filtername is None
199 assert repo.filtername is None
200 f.write(repo[self.originalwd].hex() + '\n')
200 f.write(repo[self.originalwd].hex() + '\n')
201 # was "dest". we now write dest per src root below.
201 # was "dest". we now write dest per src root below.
202 f.write('\n')
202 f.write('\n')
203 f.write(repo[self.external].hex() + '\n')
203 f.write(repo[self.external].hex() + '\n')
204 f.write('%d\n' % int(self.collapsef))
204 f.write('%d\n' % int(self.collapsef))
205 f.write('%d\n' % int(self.keepf))
205 f.write('%d\n' % int(self.keepf))
206 f.write('%d\n' % int(self.keepbranchesf))
206 f.write('%d\n' % int(self.keepbranchesf))
207 f.write('%s\n' % (self.activebookmark or ''))
207 f.write('%s\n' % (self.activebookmark or ''))
208 destmap = self.destmap
208 destmap = self.destmap
209 for d, v in self.state.iteritems():
209 for d, v in self.state.iteritems():
210 oldrev = repo[d].hex()
210 oldrev = repo[d].hex()
211 if v >= 0:
211 if v >= 0:
212 newrev = repo[v].hex()
212 newrev = repo[v].hex()
213 else:
213 else:
214 newrev = "%d" % v
214 newrev = "%d" % v
215 destnode = repo[destmap[d]].hex()
215 destnode = repo[destmap[d]].hex()
216 f.write("%s:%s:%s\n" % (oldrev, newrev, destnode))
216 f.write("%s:%s:%s\n" % (oldrev, newrev, destnode))
217 repo.ui.debug('rebase status stored\n')
217 repo.ui.debug('rebase status stored\n')
218
218
219 def restorestatus(self):
219 def restorestatus(self):
220 """Restore a previously stored status"""
220 """Restore a previously stored status"""
221 self.prepared = True
221 self.prepared = True
222 repo = self.repo
222 repo = self.repo
223 assert repo.filtername is None
223 assert repo.filtername is None
224 keepbranches = None
224 keepbranches = None
225 legacydest = None
225 legacydest = None
226 collapse = False
226 collapse = False
227 external = nullrev
227 external = nullrev
228 activebookmark = None
228 activebookmark = None
229 state = {}
229 state = {}
230 destmap = {}
230 destmap = {}
231
231
232 try:
232 try:
233 f = repo.vfs("rebasestate")
233 f = repo.vfs("rebasestate")
234 for i, l in enumerate(f.read().splitlines()):
234 for i, l in enumerate(f.read().splitlines()):
235 if i == 0:
235 if i == 0:
236 originalwd = repo[l].rev()
236 originalwd = repo[l].rev()
237 elif i == 1:
237 elif i == 1:
238 # this line should be empty in newer version. but legacy
238 # this line should be empty in newer version. but legacy
239 # clients may still use it
239 # clients may still use it
240 if l:
240 if l:
241 legacydest = repo[l].rev()
241 legacydest = repo[l].rev()
242 elif i == 2:
242 elif i == 2:
243 external = repo[l].rev()
243 external = repo[l].rev()
244 elif i == 3:
244 elif i == 3:
245 collapse = bool(int(l))
245 collapse = bool(int(l))
246 elif i == 4:
246 elif i == 4:
247 keep = bool(int(l))
247 keep = bool(int(l))
248 elif i == 5:
248 elif i == 5:
249 keepbranches = bool(int(l))
249 keepbranches = bool(int(l))
250 elif i == 6 and not (len(l) == 81 and ':' in l):
250 elif i == 6 and not (len(l) == 81 and ':' in l):
251 # line 6 is a recent addition, so for backwards
251 # line 6 is a recent addition, so for backwards
252 # compatibility check that the line doesn't look like the
252 # compatibility check that the line doesn't look like the
253 # oldrev:newrev lines
253 # oldrev:newrev lines
254 activebookmark = l
254 activebookmark = l
255 else:
255 else:
256 args = l.split(':')
256 args = l.split(':')
257 oldrev = args[0]
257 oldrev = args[0]
258 newrev = args[1]
258 newrev = args[1]
259 if newrev in legacystates:
259 if newrev in legacystates:
260 continue
260 continue
261 if len(args) > 2:
261 if len(args) > 2:
262 destnode = args[2]
262 destnode = args[2]
263 else:
263 else:
264 destnode = legacydest
264 destnode = legacydest
265 destmap[repo[oldrev].rev()] = repo[destnode].rev()
265 destmap[repo[oldrev].rev()] = repo[destnode].rev()
266 if newrev in (nullid, revtodostr):
266 if newrev in (nullid, revtodostr):
267 state[repo[oldrev].rev()] = revtodo
267 state[repo[oldrev].rev()] = revtodo
268 # Legacy compat special case
268 # Legacy compat special case
269 else:
269 else:
270 state[repo[oldrev].rev()] = repo[newrev].rev()
270 state[repo[oldrev].rev()] = repo[newrev].rev()
271
271
272 except IOError as err:
272 except IOError as err:
273 if err.errno != errno.ENOENT:
273 if err.errno != errno.ENOENT:
274 raise
274 raise
275 cmdutil.wrongtooltocontinue(repo, _('rebase'))
275 cmdutil.wrongtooltocontinue(repo, _('rebase'))
276
276
277 if keepbranches is None:
277 if keepbranches is None:
278 raise error.Abort(_('.hg/rebasestate is incomplete'))
278 raise error.Abort(_('.hg/rebasestate is incomplete'))
279
279
280 skipped = set()
280 skipped = set()
281 # recompute the set of skipped revs
281 # recompute the set of skipped revs
282 if not collapse:
282 if not collapse:
283 seen = set(destmap.values())
283 seen = set(destmap.values())
284 for old, new in sorted(state.items()):
284 for old, new in sorted(state.items()):
285 if new != revtodo and new in seen:
285 if new != revtodo and new in seen:
286 skipped.add(old)
286 skipped.add(old)
287 seen.add(new)
287 seen.add(new)
288 repo.ui.debug('computed skipped revs: %s\n' %
288 repo.ui.debug('computed skipped revs: %s\n' %
289 (' '.join('%d' % r for r in sorted(skipped)) or ''))
289 (' '.join('%d' % r for r in sorted(skipped)) or ''))
290 repo.ui.debug('rebase status resumed\n')
290 repo.ui.debug('rebase status resumed\n')
291
291
292 self.originalwd = originalwd
292 self.originalwd = originalwd
293 self.destmap = destmap
293 self.destmap = destmap
294 self.state = state
294 self.state = state
295 self.skipped = skipped
295 self.skipped = skipped
296 self.collapsef = collapse
296 self.collapsef = collapse
297 self.keepf = keep
297 self.keepf = keep
298 self.keepbranchesf = keepbranches
298 self.keepbranchesf = keepbranches
299 self.external = external
299 self.external = external
300 self.activebookmark = activebookmark
300 self.activebookmark = activebookmark
301
301
302 def _handleskippingobsolete(self, obsoleterevs, destmap):
302 def _handleskippingobsolete(self, obsoleterevs, destmap):
303 """Compute structures necessary for skipping obsolete revisions
303 """Compute structures necessary for skipping obsolete revisions
304
304
305 obsoleterevs: iterable of all obsolete revisions in rebaseset
305 obsoleterevs: iterable of all obsolete revisions in rebaseset
306 destmap: {srcrev: destrev} destination revisions
306 destmap: {srcrev: destrev} destination revisions
307 """
307 """
308 self.obsoletenotrebased = {}
308 self.obsoletenotrebased = {}
309 if not self.ui.configbool('experimental', 'rebaseskipobsolete'):
309 if not self.ui.configbool('experimental', 'rebaseskipobsolete'):
310 return
310 return
311 obsoleteset = set(obsoleterevs)
311 obsoleteset = set(obsoleterevs)
312 (self.obsoletenotrebased,
312 (self.obsoletenotrebased,
313 self.obsoletewithoutsuccessorindestination,
313 self.obsoletewithoutsuccessorindestination,
314 obsoleteextinctsuccessors) = _computeobsoletenotrebased(
314 obsoleteextinctsuccessors) = _computeobsoletenotrebased(
315 self.repo, obsoleteset, destmap)
315 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 skippedset.update(obsoleteextinctsuccessors)
318 skippedset.update(obsoleteextinctsuccessors)
319 _checkobsrebase(self.repo, self.ui, obsoleteset, skippedset)
319 _checkobsrebase(self.repo, self.ui, obsoleteset, skippedset)
320
320
321 def _prepareabortorcontinue(self, isabort):
321 def _prepareabortorcontinue(self, isabort):
322 try:
322 try:
323 self.restorestatus()
323 self.restorestatus()
324 self.collapsemsg = restorecollapsemsg(self.repo, isabort)
324 self.collapsemsg = restorecollapsemsg(self.repo, isabort)
325 except error.RepoLookupError:
325 except error.RepoLookupError:
326 if isabort:
326 if isabort:
327 clearstatus(self.repo)
327 clearstatus(self.repo)
328 clearcollapsemsg(self.repo)
328 clearcollapsemsg(self.repo)
329 self.repo.ui.warn(_('rebase aborted (no revision is removed,'
329 self.repo.ui.warn(_('rebase aborted (no revision is removed,'
330 ' only broken state is cleared)\n'))
330 ' only broken state is cleared)\n'))
331 return 0
331 return 0
332 else:
332 else:
333 msg = _('cannot continue inconsistent rebase')
333 msg = _('cannot continue inconsistent rebase')
334 hint = _('use "hg rebase --abort" to clear broken state')
334 hint = _('use "hg rebase --abort" to clear broken state')
335 raise error.Abort(msg, hint=hint)
335 raise error.Abort(msg, hint=hint)
336 if isabort:
336 if isabort:
337 return abort(self.repo, self.originalwd, self.destmap,
337 return abort(self.repo, self.originalwd, self.destmap,
338 self.state, activebookmark=self.activebookmark)
338 self.state, activebookmark=self.activebookmark)
339
339
340 def _preparenewrebase(self, destmap):
340 def _preparenewrebase(self, destmap):
341 if not destmap:
341 if not destmap:
342 return _nothingtorebase()
342 return _nothingtorebase()
343
343
344 rebaseset = destmap.keys()
344 rebaseset = destmap.keys()
345 allowunstable = obsolete.isenabled(self.repo, obsolete.allowunstableopt)
345 allowunstable = obsolete.isenabled(self.repo, obsolete.allowunstableopt)
346 if (not (self.keepf or allowunstable)
346 if (not (self.keepf or allowunstable)
347 and self.repo.revs('first(children(%ld) - %ld)',
347 and self.repo.revs('first(children(%ld) - %ld)',
348 rebaseset, rebaseset)):
348 rebaseset, rebaseset)):
349 raise error.Abort(
349 raise error.Abort(
350 _("can't remove original changesets with"
350 _("can't remove original changesets with"
351 " unrebased descendants"),
351 " unrebased descendants"),
352 hint=_('use --keep to keep original changesets'))
352 hint=_('use --keep to keep original changesets'))
353
353
354 result = buildstate(self.repo, destmap, self.collapsef)
354 result = buildstate(self.repo, destmap, self.collapsef)
355
355
356 if not result:
356 if not result:
357 # Empty state built, nothing to rebase
357 # Empty state built, nothing to rebase
358 self.ui.status(_('nothing to rebase\n'))
358 self.ui.status(_('nothing to rebase\n'))
359 return _nothingtorebase()
359 return _nothingtorebase()
360
360
361 for root in self.repo.set('roots(%ld)', rebaseset):
361 for root in self.repo.set('roots(%ld)', rebaseset):
362 if not self.keepf and not root.mutable():
362 if not self.keepf and not root.mutable():
363 raise error.Abort(_("can't rebase public changeset %s")
363 raise error.Abort(_("can't rebase public changeset %s")
364 % root,
364 % root,
365 hint=_("see 'hg help phases' for details"))
365 hint=_("see 'hg help phases' for details"))
366
366
367 (self.originalwd, self.destmap, self.state) = result
367 (self.originalwd, self.destmap, self.state) = result
368 if self.collapsef:
368 if self.collapsef:
369 dests = set(self.destmap.values())
369 dests = set(self.destmap.values())
370 if len(dests) != 1:
370 if len(dests) != 1:
371 raise error.Abort(
371 raise error.Abort(
372 _('--collapse does not work with multiple destinations'))
372 _('--collapse does not work with multiple destinations'))
373 destrev = next(iter(dests))
373 destrev = next(iter(dests))
374 destancestors = self.repo.changelog.ancestors([destrev],
374 destancestors = self.repo.changelog.ancestors([destrev],
375 inclusive=True)
375 inclusive=True)
376 self.external = externalparent(self.repo, self.state, destancestors)
376 self.external = externalparent(self.repo, self.state, destancestors)
377
377
378 for destrev in sorted(set(destmap.values())):
378 for destrev in sorted(set(destmap.values())):
379 dest = self.repo[destrev]
379 dest = self.repo[destrev]
380 if dest.closesbranch() and not self.keepbranchesf:
380 if dest.closesbranch() and not self.keepbranchesf:
381 self.ui.status(_('reopening closed branch head %s\n') % dest)
381 self.ui.status(_('reopening closed branch head %s\n') % dest)
382
382
383 self.prepared = True
383 self.prepared = True
384
384
385 def _assignworkingcopy(self):
385 def _assignworkingcopy(self):
386 if self.inmemory:
386 if self.inmemory:
387 from mercurial.context import overlayworkingctx
387 from mercurial.context import overlayworkingctx
388 self.wctx = overlayworkingctx(self.repo)
388 self.wctx = overlayworkingctx(self.repo)
389 self.repo.ui.debug("rebasing in-memory\n")
389 self.repo.ui.debug("rebasing in-memory\n")
390 else:
390 else:
391 self.wctx = self.repo[None]
391 self.wctx = self.repo[None]
392 self.repo.ui.debug("rebasing on disk\n")
392 self.repo.ui.debug("rebasing on disk\n")
393 self.repo.ui.log("rebase", "", rebase_imm_used=self.wctx.isinmemory())
393 self.repo.ui.log("rebase", "", rebase_imm_used=self.wctx.isinmemory())
394
394
395 def _performrebase(self, tr):
395 def _performrebase(self, tr):
396 self._assignworkingcopy()
396 self._assignworkingcopy()
397 repo, ui = self.repo, self.ui
397 repo, ui = self.repo, self.ui
398 if self.keepbranchesf:
398 if self.keepbranchesf:
399 # insert _savebranch at the start of extrafns so if
399 # insert _savebranch at the start of extrafns so if
400 # there's a user-provided extrafn it can clobber branch if
400 # there's a user-provided extrafn it can clobber branch if
401 # desired
401 # desired
402 self.extrafns.insert(0, _savebranch)
402 self.extrafns.insert(0, _savebranch)
403 if self.collapsef:
403 if self.collapsef:
404 branches = set()
404 branches = set()
405 for rev in self.state:
405 for rev in self.state:
406 branches.add(repo[rev].branch())
406 branches.add(repo[rev].branch())
407 if len(branches) > 1:
407 if len(branches) > 1:
408 raise error.Abort(_('cannot collapse multiple named '
408 raise error.Abort(_('cannot collapse multiple named '
409 'branches'))
409 'branches'))
410
410
411 # Calculate self.obsoletenotrebased
411 # Calculate self.obsoletenotrebased
412 obsrevs = _filterobsoleterevs(self.repo, self.state)
412 obsrevs = _filterobsoleterevs(self.repo, self.state)
413 self._handleskippingobsolete(obsrevs, self.destmap)
413 self._handleskippingobsolete(obsrevs, self.destmap)
414
414
415 # Keep track of the active bookmarks in order to reset them later
415 # Keep track of the active bookmarks in order to reset them later
416 self.activebookmark = self.activebookmark or repo._activebookmark
416 self.activebookmark = self.activebookmark or repo._activebookmark
417 if self.activebookmark:
417 if self.activebookmark:
418 bookmarks.deactivate(repo)
418 bookmarks.deactivate(repo)
419
419
420 # Store the state before we begin so users can run 'hg rebase --abort'
420 # Store the state before we begin so users can run 'hg rebase --abort'
421 # if we fail before the transaction closes.
421 # if we fail before the transaction closes.
422 self.storestatus()
422 self.storestatus()
423
423
424 cands = [k for k, v in self.state.iteritems() if v == revtodo]
424 cands = [k for k, v in self.state.iteritems() if v == revtodo]
425 total = len(cands)
425 total = len(cands)
426 posholder = [0]
426 posholder = [0]
427 def progress(ctx):
427 def progress(ctx):
428 posholder[0] += 1
428 posholder[0] += 1
429 self.repo.ui.progress(_("rebasing"), posholder[0],
429 self.repo.ui.progress(_("rebasing"), posholder[0],
430 ("%d:%s" % (ctx.rev(), ctx)),
430 ("%d:%s" % (ctx.rev(), ctx)),
431 _('changesets'), total)
431 _('changesets'), total)
432 allowdivergence = self.ui.configbool(
432 allowdivergence = self.ui.configbool(
433 'experimental', 'evolution.allowdivergence')
433 'experimental', 'evolution.allowdivergence')
434 for subset in sortsource(self.destmap):
434 for subset in sortsource(self.destmap):
435 sortedrevs = self.repo.revs('sort(%ld, -topo)', subset)
435 sortedrevs = self.repo.revs('sort(%ld, -topo)', subset)
436 if not allowdivergence:
436 if not allowdivergence:
437 sortedrevs -= self.repo.revs(
437 sortedrevs -= self.repo.revs(
438 'descendants(%ld) and not %ld',
438 'descendants(%ld) and not %ld',
439 self.obsoletewithoutsuccessorindestination,
439 self.obsoletewithoutsuccessorindestination,
440 self.obsoletewithoutsuccessorindestination,
440 self.obsoletewithoutsuccessorindestination,
441 )
441 )
442 for rev in sortedrevs:
442 for rev in sortedrevs:
443 self._rebasenode(tr, rev, allowdivergence, progress)
443 self._rebasenode(tr, rev, allowdivergence, progress)
444 ui.progress(_('rebasing'), None)
444 ui.progress(_('rebasing'), None)
445 ui.note(_('rebase merging completed\n'))
445 ui.note(_('rebase merging completed\n'))
446
446
447 def _rebasenode(self, tr, rev, allowdivergence, progressfn):
447 def _rebasenode(self, tr, rev, allowdivergence, progressfn):
448 repo, ui, opts = self.repo, self.ui, self.opts
448 repo, ui, opts = self.repo, self.ui, self.opts
449 dest = self.destmap[rev]
449 dest = self.destmap[rev]
450 ctx = repo[rev]
450 ctx = repo[rev]
451 desc = _ctxdesc(ctx)
451 desc = _ctxdesc(ctx)
452 if self.state[rev] == rev:
452 if self.state[rev] == rev:
453 ui.status(_('already rebased %s\n') % desc)
453 ui.status(_('already rebased %s\n') % desc)
454 elif (not allowdivergence
454 elif (not allowdivergence
455 and rev in self.obsoletewithoutsuccessorindestination):
455 and rev in self.obsoletewithoutsuccessorindestination):
456 msg = _('note: not rebasing %s and its descendants as '
456 msg = _('note: not rebasing %s and its descendants as '
457 'this would cause divergence\n') % desc
457 'this would cause divergence\n') % desc
458 repo.ui.status(msg)
458 repo.ui.status(msg)
459 self.skipped.add(rev)
459 self.skipped.add(rev)
460 elif rev in self.obsoletenotrebased:
460 elif rev in self.obsoletenotrebased:
461 succ = self.obsoletenotrebased[rev]
461 succ = self.obsoletenotrebased[rev]
462 if succ is None:
462 if succ is None:
463 msg = _('note: not rebasing %s, it has no '
463 msg = _('note: not rebasing %s, it has no '
464 'successor\n') % desc
464 'successor\n') % desc
465 else:
465 else:
466 succdesc = _ctxdesc(repo[succ])
466 succdesc = _ctxdesc(repo[succ])
467 msg = (_('note: not rebasing %s, already in '
467 msg = (_('note: not rebasing %s, already in '
468 'destination as %s\n') % (desc, succdesc))
468 'destination as %s\n') % (desc, succdesc))
469 repo.ui.status(msg)
469 repo.ui.status(msg)
470 # Make clearrebased aware state[rev] is not a true successor
470 # Make clearrebased aware state[rev] is not a true successor
471 self.skipped.add(rev)
471 self.skipped.add(rev)
472 # Record rev as moved to its desired destination in self.state.
472 # Record rev as moved to its desired destination in self.state.
473 # This helps bookmark and working parent movement.
473 # This helps bookmark and working parent movement.
474 dest = max(adjustdest(repo, rev, self.destmap, self.state,
474 dest = max(adjustdest(repo, rev, self.destmap, self.state,
475 self.skipped))
475 self.skipped))
476 self.state[rev] = dest
476 self.state[rev] = dest
477 elif self.state[rev] == revtodo:
477 elif self.state[rev] == revtodo:
478 ui.status(_('rebasing %s\n') % desc)
478 ui.status(_('rebasing %s\n') % desc)
479 progressfn(ctx)
479 progressfn(ctx)
480 p1, p2, base = defineparents(repo, rev, self.destmap,
480 p1, p2, base = defineparents(repo, rev, self.destmap,
481 self.state, self.skipped,
481 self.state, self.skipped,
482 self.obsoletenotrebased)
482 self.obsoletenotrebased)
483 self.storestatus(tr=tr)
483 self.storestatus(tr=tr)
484 if len(repo[None].parents()) == 2:
484 if len(repo[None].parents()) == 2:
485 repo.ui.debug('resuming interrupted rebase\n')
485 repo.ui.debug('resuming interrupted rebase\n')
486 else:
486 else:
487 overrides = {('ui', 'forcemerge'): opts.get('tool', '')}
487 overrides = {('ui', 'forcemerge'): opts.get('tool', '')}
488 with ui.configoverride(overrides, 'rebase'):
488 with ui.configoverride(overrides, 'rebase'):
489 stats = rebasenode(repo, rev, p1, base, self.collapsef,
489 stats = rebasenode(repo, rev, p1, base, self.collapsef,
490 dest, wctx=self.wctx)
490 dest, wctx=self.wctx)
491 if stats[3] > 0:
491 if stats[3] > 0:
492 if self.wctx.isinmemory():
492 if self.wctx.isinmemory():
493 raise error.InMemoryMergeConflictsError()
493 raise error.InMemoryMergeConflictsError()
494 else:
494 else:
495 raise error.InterventionRequired(
495 raise error.InterventionRequired(
496 _('unresolved conflicts (see hg '
496 _('unresolved conflicts (see hg '
497 'resolve, then hg rebase --continue)'))
497 'resolve, then hg rebase --continue)'))
498 if not self.collapsef:
498 if not self.collapsef:
499 merging = p2 != nullrev
499 merging = p2 != nullrev
500 editform = cmdutil.mergeeditform(merging, 'rebase')
500 editform = cmdutil.mergeeditform(merging, 'rebase')
501 editor = cmdutil.getcommiteditor(editform=editform,
501 editor = cmdutil.getcommiteditor(editform=editform,
502 **pycompat.strkwargs(opts))
502 **pycompat.strkwargs(opts))
503 if self.wctx.isinmemory():
503 if self.wctx.isinmemory():
504 newnode = concludememorynode(repo, rev, p1, p2,
504 newnode = concludememorynode(repo, rev, p1, p2,
505 wctx=self.wctx,
505 wctx=self.wctx,
506 extrafn=_makeextrafn(self.extrafns),
506 extrafn=_makeextrafn(self.extrafns),
507 editor=editor,
507 editor=editor,
508 keepbranches=self.keepbranchesf,
508 keepbranches=self.keepbranchesf,
509 date=self.date)
509 date=self.date)
510 mergemod.mergestate.clean(repo)
510 mergemod.mergestate.clean(repo)
511 else:
511 else:
512 newnode = concludenode(repo, rev, p1, p2,
512 newnode = concludenode(repo, rev, p1, p2,
513 extrafn=_makeextrafn(self.extrafns),
513 extrafn=_makeextrafn(self.extrafns),
514 editor=editor,
514 editor=editor,
515 keepbranches=self.keepbranchesf,
515 keepbranches=self.keepbranchesf,
516 date=self.date)
516 date=self.date)
517
517
518 if newnode is None:
518 if newnode is None:
519 # If it ended up being a no-op commit, then the normal
519 # If it ended up being a no-op commit, then the normal
520 # merge state clean-up path doesn't happen, so do it
520 # merge state clean-up path doesn't happen, so do it
521 # here. Fix issue5494
521 # here. Fix issue5494
522 mergemod.mergestate.clean(repo)
522 mergemod.mergestate.clean(repo)
523 else:
523 else:
524 # Skip commit if we are collapsing
524 # Skip commit if we are collapsing
525 if self.wctx.isinmemory():
525 if self.wctx.isinmemory():
526 self.wctx.setbase(repo[p1])
526 self.wctx.setbase(repo[p1])
527 else:
527 else:
528 repo.setparents(repo[p1].node())
528 repo.setparents(repo[p1].node())
529 newnode = None
529 newnode = None
530 # Update the state
530 # Update the state
531 if newnode is not None:
531 if newnode is not None:
532 self.state[rev] = repo[newnode].rev()
532 self.state[rev] = repo[newnode].rev()
533 ui.debug('rebased as %s\n' % short(newnode))
533 ui.debug('rebased as %s\n' % short(newnode))
534 else:
534 else:
535 if not self.collapsef:
535 if not self.collapsef:
536 ui.warn(_('note: rebase of %d:%s created no changes '
536 ui.warn(_('note: rebase of %d:%s created no changes '
537 'to commit\n') % (rev, ctx))
537 'to commit\n') % (rev, ctx))
538 self.skipped.add(rev)
538 self.skipped.add(rev)
539 self.state[rev] = p1
539 self.state[rev] = p1
540 ui.debug('next revision set to %d\n' % p1)
540 ui.debug('next revision set to %d\n' % p1)
541 else:
541 else:
542 ui.status(_('already rebased %s as %s\n') %
542 ui.status(_('already rebased %s as %s\n') %
543 (desc, repo[self.state[rev]]))
543 (desc, repo[self.state[rev]]))
544
544
545 def _finishrebase(self):
545 def _finishrebase(self):
546 repo, ui, opts = self.repo, self.ui, self.opts
546 repo, ui, opts = self.repo, self.ui, self.opts
547 fm = ui.formatter('rebase', opts)
547 fm = ui.formatter('rebase', opts)
548 fm.startitem()
548 fm.startitem()
549 if self.collapsef:
549 if self.collapsef:
550 p1, p2, _base = defineparents(repo, min(self.state), self.destmap,
550 p1, p2, _base = defineparents(repo, min(self.state), self.destmap,
551 self.state, self.skipped,
551 self.state, self.skipped,
552 self.obsoletenotrebased)
552 self.obsoletenotrebased)
553 editopt = opts.get('edit')
553 editopt = opts.get('edit')
554 editform = 'rebase.collapse'
554 editform = 'rebase.collapse'
555 if self.collapsemsg:
555 if self.collapsemsg:
556 commitmsg = self.collapsemsg
556 commitmsg = self.collapsemsg
557 else:
557 else:
558 commitmsg = 'Collapsed revision'
558 commitmsg = 'Collapsed revision'
559 for rebased in sorted(self.state):
559 for rebased in sorted(self.state):
560 if rebased not in self.skipped:
560 if rebased not in self.skipped:
561 commitmsg += '\n* %s' % repo[rebased].description()
561 commitmsg += '\n* %s' % repo[rebased].description()
562 editopt = True
562 editopt = True
563 editor = cmdutil.getcommiteditor(edit=editopt, editform=editform)
563 editor = cmdutil.getcommiteditor(edit=editopt, editform=editform)
564 revtoreuse = max(self.state)
564 revtoreuse = max(self.state)
565
565
566 if self.inmemory:
566 if self.inmemory:
567 newnode = concludememorynode(repo, revtoreuse, p1,
567 newnode = concludememorynode(repo, revtoreuse, p1,
568 self.external,
568 self.external,
569 commitmsg=commitmsg,
569 commitmsg=commitmsg,
570 extrafn=_makeextrafn(self.extrafns),
570 extrafn=_makeextrafn(self.extrafns),
571 editor=editor,
571 editor=editor,
572 keepbranches=self.keepbranchesf,
572 keepbranches=self.keepbranchesf,
573 date=self.date, wctx=self.wctx)
573 date=self.date, wctx=self.wctx)
574 else:
574 else:
575 newnode = concludenode(repo, revtoreuse, p1, self.external,
575 newnode = concludenode(repo, revtoreuse, p1, self.external,
576 commitmsg=commitmsg,
576 commitmsg=commitmsg,
577 extrafn=_makeextrafn(self.extrafns),
577 extrafn=_makeextrafn(self.extrafns),
578 editor=editor,
578 editor=editor,
579 keepbranches=self.keepbranchesf,
579 keepbranches=self.keepbranchesf,
580 date=self.date)
580 date=self.date)
581
581
582 if newnode is None:
582 if newnode is None:
583 # If it ended up being a no-op commit, then the normal
583 # If it ended up being a no-op commit, then the normal
584 # merge state clean-up path doesn't happen, so do it
584 # merge state clean-up path doesn't happen, so do it
585 # here. Fix issue5494
585 # here. Fix issue5494
586 mergemod.mergestate.clean(repo)
586 mergemod.mergestate.clean(repo)
587 if newnode is not None:
587 if newnode is not None:
588 newrev = repo[newnode].rev()
588 newrev = repo[newnode].rev()
589 for oldrev in self.state:
589 for oldrev in self.state:
590 self.state[oldrev] = newrev
590 self.state[oldrev] = newrev
591
591
592 if 'qtip' in repo.tags():
592 if 'qtip' in repo.tags():
593 updatemq(repo, self.state, self.skipped,
593 updatemq(repo, self.state, self.skipped,
594 **pycompat.strkwargs(opts))
594 **pycompat.strkwargs(opts))
595
595
596 # restore original working directory
596 # restore original working directory
597 # (we do this before stripping)
597 # (we do this before stripping)
598 newwd = self.state.get(self.originalwd, self.originalwd)
598 newwd = self.state.get(self.originalwd, self.originalwd)
599 if newwd < 0:
599 if newwd < 0:
600 # original directory is a parent of rebase set root or ignored
600 # original directory is a parent of rebase set root or ignored
601 newwd = self.originalwd
601 newwd = self.originalwd
602 if (newwd not in [c.rev() for c in repo[None].parents()] and
602 if newwd not in [c.rev() for c in repo[None].parents()]:
603 not self.inmemory):
604 ui.note(_("update back to initial working directory parent\n"))
603 ui.note(_("update back to initial working directory parent\n"))
605 hg.updaterepo(repo, newwd, False)
604 hg.updaterepo(repo, newwd, False)
606
605
607 collapsedas = None
606 collapsedas = None
608 if self.collapsef and not self.keepf:
607 if self.collapsef and not self.keepf:
609 collapsedas = newnode
608 collapsedas = newnode
610 clearrebased(ui, repo, self.destmap, self.state, self.skipped,
609 clearrebased(ui, repo, self.destmap, self.state, self.skipped,
611 collapsedas, self.keepf, fm=fm)
610 collapsedas, self.keepf, fm=fm)
612
611
613 clearstatus(repo)
612 clearstatus(repo)
614 clearcollapsemsg(repo)
613 clearcollapsemsg(repo)
615
614
616 ui.note(_("rebase completed\n"))
615 ui.note(_("rebase completed\n"))
617 util.unlinkpath(repo.sjoin('undo'), ignoremissing=True)
616 util.unlinkpath(repo.sjoin('undo'), ignoremissing=True)
618 if self.skipped:
617 if self.skipped:
619 skippedlen = len(self.skipped)
618 skippedlen = len(self.skipped)
620 ui.note(_("%d revisions have been skipped\n") % skippedlen)
619 ui.note(_("%d revisions have been skipped\n") % skippedlen)
621 fm.end()
620 fm.end()
622
621
623 if (self.activebookmark and self.activebookmark in repo._bookmarks and
622 if (self.activebookmark and self.activebookmark in repo._bookmarks and
624 repo['.'].node() == repo._bookmarks[self.activebookmark]):
623 repo['.'].node() == repo._bookmarks[self.activebookmark]):
625 bookmarks.activate(repo, self.activebookmark)
624 bookmarks.activate(repo, self.activebookmark)
626
625
627 @command('rebase',
626 @command('rebase',
628 [('s', 'source', '',
627 [('s', 'source', '',
629 _('rebase the specified changeset and descendants'), _('REV')),
628 _('rebase the specified changeset and descendants'), _('REV')),
630 ('b', 'base', '',
629 ('b', 'base', '',
631 _('rebase everything from branching point of specified changeset'),
630 _('rebase everything from branching point of specified changeset'),
632 _('REV')),
631 _('REV')),
633 ('r', 'rev', [],
632 ('r', 'rev', [],
634 _('rebase these revisions'),
633 _('rebase these revisions'),
635 _('REV')),
634 _('REV')),
636 ('d', 'dest', '',
635 ('d', 'dest', '',
637 _('rebase onto the specified changeset'), _('REV')),
636 _('rebase onto the specified changeset'), _('REV')),
638 ('', 'collapse', False, _('collapse the rebased changesets')),
637 ('', 'collapse', False, _('collapse the rebased changesets')),
639 ('m', 'message', '',
638 ('m', 'message', '',
640 _('use text as collapse commit message'), _('TEXT')),
639 _('use text as collapse commit message'), _('TEXT')),
641 ('e', 'edit', False, _('invoke editor on commit messages')),
640 ('e', 'edit', False, _('invoke editor on commit messages')),
642 ('l', 'logfile', '',
641 ('l', 'logfile', '',
643 _('read collapse commit message from file'), _('FILE')),
642 _('read collapse commit message from file'), _('FILE')),
644 ('k', 'keep', False, _('keep original changesets')),
643 ('k', 'keep', False, _('keep original changesets')),
645 ('', 'keepbranches', False, _('keep original branch names')),
644 ('', 'keepbranches', False, _('keep original branch names')),
646 ('D', 'detach', False, _('(DEPRECATED)')),
645 ('D', 'detach', False, _('(DEPRECATED)')),
647 ('i', 'interactive', False, _('(DEPRECATED)')),
646 ('i', 'interactive', False, _('(DEPRECATED)')),
648 ('t', 'tool', '', _('specify merge tool')),
647 ('t', 'tool', '', _('specify merge tool')),
649 ('c', 'continue', False, _('continue an interrupted rebase')),
648 ('c', 'continue', False, _('continue an interrupted rebase')),
650 ('a', 'abort', False, _('abort an interrupted rebase'))] +
649 ('a', 'abort', False, _('abort an interrupted rebase'))] +
651 cmdutil.formatteropts,
650 cmdutil.formatteropts,
652 _('[-s REV | -b REV] [-d REV] [OPTION]'))
651 _('[-s REV | -b REV] [-d REV] [OPTION]'))
653 def rebase(ui, repo, **opts):
652 def rebase(ui, repo, **opts):
654 """move changeset (and descendants) to a different branch
653 """move changeset (and descendants) to a different branch
655
654
656 Rebase uses repeated merging to graft changesets from one part of
655 Rebase uses repeated merging to graft changesets from one part of
657 history (the source) onto another (the destination). This can be
656 history (the source) onto another (the destination). This can be
658 useful for linearizing *local* changes relative to a master
657 useful for linearizing *local* changes relative to a master
659 development tree.
658 development tree.
660
659
661 Published commits cannot be rebased (see :hg:`help phases`).
660 Published commits cannot be rebased (see :hg:`help phases`).
662 To copy commits, see :hg:`help graft`.
661 To copy commits, see :hg:`help graft`.
663
662
664 If you don't specify a destination changeset (``-d/--dest``), rebase
663 If you don't specify a destination changeset (``-d/--dest``), rebase
665 will use the same logic as :hg:`merge` to pick a destination. if
664 will use the same logic as :hg:`merge` to pick a destination. if
666 the current branch contains exactly one other head, the other head
665 the current branch contains exactly one other head, the other head
667 is merged with by default. Otherwise, an explicit revision with
666 is merged with by default. Otherwise, an explicit revision with
668 which to merge with must be provided. (destination changeset is not
667 which to merge with must be provided. (destination changeset is not
669 modified by rebasing, but new changesets are added as its
668 modified by rebasing, but new changesets are added as its
670 descendants.)
669 descendants.)
671
670
672 Here are the ways to select changesets:
671 Here are the ways to select changesets:
673
672
674 1. Explicitly select them using ``--rev``.
673 1. Explicitly select them using ``--rev``.
675
674
676 2. Use ``--source`` to select a root changeset and include all of its
675 2. Use ``--source`` to select a root changeset and include all of its
677 descendants.
676 descendants.
678
677
679 3. Use ``--base`` to select a changeset; rebase will find ancestors
678 3. Use ``--base`` to select a changeset; rebase will find ancestors
680 and their descendants which are not also ancestors of the destination.
679 and their descendants which are not also ancestors of the destination.
681
680
682 4. If you do not specify any of ``--rev``, ``source``, or ``--base``,
681 4. If you do not specify any of ``--rev``, ``source``, or ``--base``,
683 rebase will use ``--base .`` as above.
682 rebase will use ``--base .`` as above.
684
683
685 If ``--source`` or ``--rev`` is used, special names ``SRC`` and ``ALLSRC``
684 If ``--source`` or ``--rev`` is used, special names ``SRC`` and ``ALLSRC``
686 can be used in ``--dest``. Destination would be calculated per source
685 can be used in ``--dest``. Destination would be calculated per source
687 revision with ``SRC`` substituted by that single source revision and
686 revision with ``SRC`` substituted by that single source revision and
688 ``ALLSRC`` substituted by all source revisions.
687 ``ALLSRC`` substituted by all source revisions.
689
688
690 Rebase will destroy original changesets unless you use ``--keep``.
689 Rebase will destroy original changesets unless you use ``--keep``.
691 It will also move your bookmarks (even if you do).
690 It will also move your bookmarks (even if you do).
692
691
693 Some changesets may be dropped if they do not contribute changes
692 Some changesets may be dropped if they do not contribute changes
694 (e.g. merges from the destination branch).
693 (e.g. merges from the destination branch).
695
694
696 Unlike ``merge``, rebase will do nothing if you are at the branch tip of
695 Unlike ``merge``, rebase will do nothing if you are at the branch tip of
697 a named branch with two heads. You will need to explicitly specify source
696 a named branch with two heads. You will need to explicitly specify source
698 and/or destination.
697 and/or destination.
699
698
700 If you need to use a tool to automate merge/conflict decisions, you
699 If you need to use a tool to automate merge/conflict decisions, you
701 can specify one with ``--tool``, see :hg:`help merge-tools`.
700 can specify one with ``--tool``, see :hg:`help merge-tools`.
702 As a caveat: the tool will not be used to mediate when a file was
701 As a caveat: the tool will not be used to mediate when a file was
703 deleted, there is no hook presently available for this.
702 deleted, there is no hook presently available for this.
704
703
705 If a rebase is interrupted to manually resolve a conflict, it can be
704 If a rebase is interrupted to manually resolve a conflict, it can be
706 continued with --continue/-c or aborted with --abort/-a.
705 continued with --continue/-c or aborted with --abort/-a.
707
706
708 .. container:: verbose
707 .. container:: verbose
709
708
710 Examples:
709 Examples:
711
710
712 - move "local changes" (current commit back to branching point)
711 - move "local changes" (current commit back to branching point)
713 to the current branch tip after a pull::
712 to the current branch tip after a pull::
714
713
715 hg rebase
714 hg rebase
716
715
717 - move a single changeset to the stable branch::
716 - move a single changeset to the stable branch::
718
717
719 hg rebase -r 5f493448 -d stable
718 hg rebase -r 5f493448 -d stable
720
719
721 - splice a commit and all its descendants onto another part of history::
720 - splice a commit and all its descendants onto another part of history::
722
721
723 hg rebase --source c0c3 --dest 4cf9
722 hg rebase --source c0c3 --dest 4cf9
724
723
725 - rebase everything on a branch marked by a bookmark onto the
724 - rebase everything on a branch marked by a bookmark onto the
726 default branch::
725 default branch::
727
726
728 hg rebase --base myfeature --dest default
727 hg rebase --base myfeature --dest default
729
728
730 - collapse a sequence of changes into a single commit::
729 - collapse a sequence of changes into a single commit::
731
730
732 hg rebase --collapse -r 1520:1525 -d .
731 hg rebase --collapse -r 1520:1525 -d .
733
732
734 - move a named branch while preserving its name::
733 - move a named branch while preserving its name::
735
734
736 hg rebase -r "branch(featureX)" -d 1.3 --keepbranches
735 hg rebase -r "branch(featureX)" -d 1.3 --keepbranches
737
736
738 - stabilize orphaned changesets so history looks linear::
737 - stabilize orphaned changesets so history looks linear::
739
738
740 hg rebase -r 'orphan()-obsolete()'\
739 hg rebase -r 'orphan()-obsolete()'\
741 -d 'first(max((successors(max(roots(ALLSRC) & ::SRC)^)-obsolete())::) +\
740 -d 'first(max((successors(max(roots(ALLSRC) & ::SRC)^)-obsolete())::) +\
742 max(::((roots(ALLSRC) & ::SRC)^)-obsolete()))'
741 max(::((roots(ALLSRC) & ::SRC)^)-obsolete()))'
743
742
744 Configuration Options:
743 Configuration Options:
745
744
746 You can make rebase require a destination if you set the following config
745 You can make rebase require a destination if you set the following config
747 option::
746 option::
748
747
749 [commands]
748 [commands]
750 rebase.requiredest = True
749 rebase.requiredest = True
751
750
752 By default, rebase will close the transaction after each commit. For
751 By default, rebase will close the transaction after each commit. For
753 performance purposes, you can configure rebase to use a single transaction
752 performance purposes, you can configure rebase to use a single transaction
754 across the entire rebase. WARNING: This setting introduces a significant
753 across the entire rebase. WARNING: This setting introduces a significant
755 risk of losing the work you've done in a rebase if the rebase aborts
754 risk of losing the work you've done in a rebase if the rebase aborts
756 unexpectedly::
755 unexpectedly::
757
756
758 [rebase]
757 [rebase]
759 singletransaction = True
758 singletransaction = True
760
759
761 By default, rebase writes to the working copy, but you can configure it to
760 By default, rebase writes to the working copy, but you can configure it to
762 run in-memory for for better performance, and to allow it to run if the
761 run in-memory for for better performance, and to allow it to run if the
763 working copy is dirty::
762 working copy is dirty::
764
763
765 [rebase]
764 [rebase]
766 experimental.inmemory = True
765 experimental.inmemory = True
767
766
768 Return Values:
767 Return Values:
769
768
770 Returns 0 on success, 1 if nothing to rebase or there are
769 Returns 0 on success, 1 if nothing to rebase or there are
771 unresolved conflicts.
770 unresolved conflicts.
772
771
773 """
772 """
774 inmemory = ui.configbool('rebase', 'experimental.inmemory')
773 inmemory = ui.configbool('rebase', 'experimental.inmemory')
775 if (opts.get('continue') or opts.get('abort') or
774 if (opts.get('continue') or opts.get('abort') or
776 repo.currenttransaction() is not None):
775 repo.currenttransaction() is not None):
777 # in-memory rebase is not compatible with resuming rebases.
776 # in-memory rebase is not compatible with resuming rebases.
778 # (Or if it is run within a transaction, since the restart logic can
777 # (Or if it is run within a transaction, since the restart logic can
779 # fail the entire transaction.)
778 # fail the entire transaction.)
780 inmemory = False
779 inmemory = False
781
780
782 if inmemory:
781 if inmemory:
783 try:
782 try:
784 # in-memory merge doesn't support conflicts, so if we hit any, abort
783 # in-memory merge doesn't support conflicts, so if we hit any, abort
785 # and re-run as an on-disk merge.
784 # and re-run as an on-disk merge.
786 return _origrebase(ui, repo, inmemory=inmemory, **opts)
785 return _origrebase(ui, repo, inmemory=inmemory, **opts)
787 except error.InMemoryMergeConflictsError:
786 except error.InMemoryMergeConflictsError:
788 ui.warn(_('hit merge conflicts; re-running rebase without in-memory'
787 ui.warn(_('hit merge conflicts; re-running rebase without in-memory'
789 ' merge\n'))
788 ' merge\n'))
790 _origrebase(ui, repo, **{'abort': True})
789 _origrebase(ui, repo, **{'abort': True})
791 return _origrebase(ui, repo, inmemory=False, **opts)
790 return _origrebase(ui, repo, inmemory=False, **opts)
792 else:
791 else:
793 return _origrebase(ui, repo, **opts)
792 return _origrebase(ui, repo, **opts)
794
793
795 def _origrebase(ui, repo, inmemory=False, **opts):
794 def _origrebase(ui, repo, inmemory=False, **opts):
796 opts = pycompat.byteskwargs(opts)
795 opts = pycompat.byteskwargs(opts)
797 rbsrt = rebaseruntime(repo, ui, inmemory, opts)
796 rbsrt = rebaseruntime(repo, ui, inmemory, opts)
798
797
799 with repo.wlock(), repo.lock():
798 with repo.wlock(), repo.lock():
800 # Validate input and define rebasing points
799 # Validate input and define rebasing points
801 destf = opts.get('dest', None)
800 destf = opts.get('dest', None)
802 srcf = opts.get('source', None)
801 srcf = opts.get('source', None)
803 basef = opts.get('base', None)
802 basef = opts.get('base', None)
804 revf = opts.get('rev', [])
803 revf = opts.get('rev', [])
805 # search default destination in this space
804 # search default destination in this space
806 # used in the 'hg pull --rebase' case, see issue 5214.
805 # used in the 'hg pull --rebase' case, see issue 5214.
807 destspace = opts.get('_destspace')
806 destspace = opts.get('_destspace')
808 contf = opts.get('continue')
807 contf = opts.get('continue')
809 abortf = opts.get('abort')
808 abortf = opts.get('abort')
810 if opts.get('interactive'):
809 if opts.get('interactive'):
811 try:
810 try:
812 if extensions.find('histedit'):
811 if extensions.find('histedit'):
813 enablehistedit = ''
812 enablehistedit = ''
814 except KeyError:
813 except KeyError:
815 enablehistedit = " --config extensions.histedit="
814 enablehistedit = " --config extensions.histedit="
816 help = "hg%s help -e histedit" % enablehistedit
815 help = "hg%s help -e histedit" % enablehistedit
817 msg = _("interactive history editing is supported by the "
816 msg = _("interactive history editing is supported by the "
818 "'histedit' extension (see \"%s\")") % help
817 "'histedit' extension (see \"%s\")") % help
819 raise error.Abort(msg)
818 raise error.Abort(msg)
820
819
821 if rbsrt.collapsemsg and not rbsrt.collapsef:
820 if rbsrt.collapsemsg and not rbsrt.collapsef:
822 raise error.Abort(
821 raise error.Abort(
823 _('message can only be specified with collapse'))
822 _('message can only be specified with collapse'))
824
823
825 if contf or abortf:
824 if contf or abortf:
826 if contf and abortf:
825 if contf and abortf:
827 raise error.Abort(_('cannot use both abort and continue'))
826 raise error.Abort(_('cannot use both abort and continue'))
828 if rbsrt.collapsef:
827 if rbsrt.collapsef:
829 raise error.Abort(
828 raise error.Abort(
830 _('cannot use collapse with continue or abort'))
829 _('cannot use collapse with continue or abort'))
831 if srcf or basef or destf:
830 if srcf or basef or destf:
832 raise error.Abort(
831 raise error.Abort(
833 _('abort and continue do not allow specifying revisions'))
832 _('abort and continue do not allow specifying revisions'))
834 if abortf and opts.get('tool', False):
833 if abortf and opts.get('tool', False):
835 ui.warn(_('tool option will be ignored\n'))
834 ui.warn(_('tool option will be ignored\n'))
836 if contf:
835 if contf:
837 ms = mergemod.mergestate.read(repo)
836 ms = mergemod.mergestate.read(repo)
838 mergeutil.checkunresolved(ms)
837 mergeutil.checkunresolved(ms)
839
838
840 retcode = rbsrt._prepareabortorcontinue(abortf)
839 retcode = rbsrt._prepareabortorcontinue(abortf)
841 if retcode is not None:
840 if retcode is not None:
842 return retcode
841 return retcode
843 else:
842 else:
844 destmap = _definedestmap(ui, repo, rbsrt, destf, srcf, basef, revf,
843 destmap = _definedestmap(ui, repo, rbsrt, destf, srcf, basef, revf,
845 destspace=destspace)
844 destspace=destspace)
846 retcode = rbsrt._preparenewrebase(destmap)
845 retcode = rbsrt._preparenewrebase(destmap)
847 if retcode is not None:
846 if retcode is not None:
848 return retcode
847 return retcode
849 storecollapsemsg(repo, rbsrt.collapsemsg)
848 storecollapsemsg(repo, rbsrt.collapsemsg)
850
849
851 tr = None
850 tr = None
852
851
853 singletr = ui.configbool('rebase', 'singletransaction')
852 singletr = ui.configbool('rebase', 'singletransaction')
854 if singletr:
853 if singletr:
855 tr = repo.transaction('rebase')
854 tr = repo.transaction('rebase')
856
855
857 # If `rebase.singletransaction` is enabled, wrap the entire operation in
856 # If `rebase.singletransaction` is enabled, wrap the entire operation in
858 # one transaction here. Otherwise, transactions are obtained when
857 # one transaction here. Otherwise, transactions are obtained when
859 # committing each node, which is slower but allows partial success.
858 # committing each node, which is slower but allows partial success.
860 with util.acceptintervention(tr):
859 with util.acceptintervention(tr):
861 # Same logic for the dirstate guard, except we don't create one when
860 # Same logic for the dirstate guard, except we don't create one when
862 # rebasing in-memory (it's not needed).
861 # rebasing in-memory (it's not needed).
863 dsguard = None
862 dsguard = None
864 if singletr and not inmemory:
863 if singletr and not inmemory:
865 dsguard = dirstateguard.dirstateguard(repo, 'rebase')
864 dsguard = dirstateguard.dirstateguard(repo, 'rebase')
866 with util.acceptintervention(dsguard):
865 with util.acceptintervention(dsguard):
867 rbsrt._performrebase(tr)
866 rbsrt._performrebase(tr)
868 rbsrt._finishrebase()
867 rbsrt._finishrebase()
869
868
870 def _definedestmap(ui, repo, rbsrt, destf=None, srcf=None, basef=None,
869 def _definedestmap(ui, repo, rbsrt, destf=None, srcf=None, basef=None,
871 revf=None, destspace=None):
870 revf=None, destspace=None):
872 """use revisions argument to define destmap {srcrev: destrev}"""
871 """use revisions argument to define destmap {srcrev: destrev}"""
873 if revf is None:
872 if revf is None:
874 revf = []
873 revf = []
875
874
876 # destspace is here to work around issues with `hg pull --rebase` see
875 # destspace is here to work around issues with `hg pull --rebase` see
877 # issue5214 for details
876 # issue5214 for details
878 if srcf and basef:
877 if srcf and basef:
879 raise error.Abort(_('cannot specify both a source and a base'))
878 raise error.Abort(_('cannot specify both a source and a base'))
880 if revf and basef:
879 if revf and basef:
881 raise error.Abort(_('cannot specify both a revision and a base'))
880 raise error.Abort(_('cannot specify both a revision and a base'))
882 if revf and srcf:
881 if revf and srcf:
883 raise error.Abort(_('cannot specify both a revision and a source'))
882 raise error.Abort(_('cannot specify both a revision and a source'))
884
883
885 if not rbsrt.inmemory:
884 if not rbsrt.inmemory:
886 cmdutil.checkunfinished(repo)
885 cmdutil.checkunfinished(repo)
887 cmdutil.bailifchanged(repo)
886 cmdutil.bailifchanged(repo)
888
887
889 if ui.configbool('commands', 'rebase.requiredest') and not destf:
888 if ui.configbool('commands', 'rebase.requiredest') and not destf:
890 raise error.Abort(_('you must specify a destination'),
889 raise error.Abort(_('you must specify a destination'),
891 hint=_('use: hg rebase -d REV'))
890 hint=_('use: hg rebase -d REV'))
892
891
893 dest = None
892 dest = None
894
893
895 if revf:
894 if revf:
896 rebaseset = scmutil.revrange(repo, revf)
895 rebaseset = scmutil.revrange(repo, revf)
897 if not rebaseset:
896 if not rebaseset:
898 ui.status(_('empty "rev" revision set - nothing to rebase\n'))
897 ui.status(_('empty "rev" revision set - nothing to rebase\n'))
899 return None
898 return None
900 elif srcf:
899 elif srcf:
901 src = scmutil.revrange(repo, [srcf])
900 src = scmutil.revrange(repo, [srcf])
902 if not src:
901 if not src:
903 ui.status(_('empty "source" revision set - nothing to rebase\n'))
902 ui.status(_('empty "source" revision set - nothing to rebase\n'))
904 return None
903 return None
905 rebaseset = repo.revs('(%ld)::', src)
904 rebaseset = repo.revs('(%ld)::', src)
906 assert rebaseset
905 assert rebaseset
907 else:
906 else:
908 base = scmutil.revrange(repo, [basef or '.'])
907 base = scmutil.revrange(repo, [basef or '.'])
909 if not base:
908 if not base:
910 ui.status(_('empty "base" revision set - '
909 ui.status(_('empty "base" revision set - '
911 "can't compute rebase set\n"))
910 "can't compute rebase set\n"))
912 return None
911 return None
913 if destf:
912 if destf:
914 # --base does not support multiple destinations
913 # --base does not support multiple destinations
915 dest = scmutil.revsingle(repo, destf)
914 dest = scmutil.revsingle(repo, destf)
916 else:
915 else:
917 dest = repo[_destrebase(repo, base, destspace=destspace)]
916 dest = repo[_destrebase(repo, base, destspace=destspace)]
918 destf = bytes(dest)
917 destf = bytes(dest)
919
918
920 roots = [] # selected children of branching points
919 roots = [] # selected children of branching points
921 bpbase = {} # {branchingpoint: [origbase]}
920 bpbase = {} # {branchingpoint: [origbase]}
922 for b in base: # group bases by branching points
921 for b in base: # group bases by branching points
923 bp = repo.revs('ancestor(%d, %d)', b, dest.rev()).first()
922 bp = repo.revs('ancestor(%d, %d)', b, dest.rev()).first()
924 bpbase[bp] = bpbase.get(bp, []) + [b]
923 bpbase[bp] = bpbase.get(bp, []) + [b]
925 if None in bpbase:
924 if None in bpbase:
926 # emulate the old behavior, showing "nothing to rebase" (a better
925 # emulate the old behavior, showing "nothing to rebase" (a better
927 # behavior may be abort with "cannot find branching point" error)
926 # behavior may be abort with "cannot find branching point" error)
928 bpbase.clear()
927 bpbase.clear()
929 for bp, bs in bpbase.iteritems(): # calculate roots
928 for bp, bs in bpbase.iteritems(): # calculate roots
930 roots += list(repo.revs('children(%d) & ancestors(%ld)', bp, bs))
929 roots += list(repo.revs('children(%d) & ancestors(%ld)', bp, bs))
931
930
932 rebaseset = repo.revs('%ld::', roots)
931 rebaseset = repo.revs('%ld::', roots)
933
932
934 if not rebaseset:
933 if not rebaseset:
935 # transform to list because smartsets are not comparable to
934 # transform to list because smartsets are not comparable to
936 # lists. This should be improved to honor laziness of
935 # lists. This should be improved to honor laziness of
937 # smartset.
936 # smartset.
938 if list(base) == [dest.rev()]:
937 if list(base) == [dest.rev()]:
939 if basef:
938 if basef:
940 ui.status(_('nothing to rebase - %s is both "base"'
939 ui.status(_('nothing to rebase - %s is both "base"'
941 ' and destination\n') % dest)
940 ' and destination\n') % dest)
942 else:
941 else:
943 ui.status(_('nothing to rebase - working directory '
942 ui.status(_('nothing to rebase - working directory '
944 'parent is also destination\n'))
943 'parent is also destination\n'))
945 elif not repo.revs('%ld - ::%d', base, dest.rev()):
944 elif not repo.revs('%ld - ::%d', base, dest.rev()):
946 if basef:
945 if basef:
947 ui.status(_('nothing to rebase - "base" %s is '
946 ui.status(_('nothing to rebase - "base" %s is '
948 'already an ancestor of destination '
947 'already an ancestor of destination '
949 '%s\n') %
948 '%s\n') %
950 ('+'.join(bytes(repo[r]) for r in base),
949 ('+'.join(bytes(repo[r]) for r in base),
951 dest))
950 dest))
952 else:
951 else:
953 ui.status(_('nothing to rebase - working '
952 ui.status(_('nothing to rebase - working '
954 'directory parent is already an '
953 'directory parent is already an '
955 'ancestor of destination %s\n') % dest)
954 'ancestor of destination %s\n') % dest)
956 else: # can it happen?
955 else: # can it happen?
957 ui.status(_('nothing to rebase from %s to %s\n') %
956 ui.status(_('nothing to rebase from %s to %s\n') %
958 ('+'.join(bytes(repo[r]) for r in base), dest))
957 ('+'.join(bytes(repo[r]) for r in base), dest))
959 return None
958 return None
960 # If rebasing the working copy parent, force in-memory merge to be off.
959
961 #
962 # This is because the extra work of checking out the newly rebased commit
963 # outweights the benefits of rebasing in-memory, and executing an extra
964 # update command adds a bit of overhead, so better to just do it on disk. In
965 # all other cases leave it on.
966 #
967 # Note that there are cases where this isn't true -- e.g., rebasing large
968 # stacks that include the WCP. However, I'm not yet sure where the cutoff
969 # is.
970 rebasingwcp = repo['.'].rev() in rebaseset
960 rebasingwcp = repo['.'].rev() in rebaseset
971 ui.log("rebase", "", rebase_rebasing_wcp=rebasingwcp)
961 ui.log("rebase", "", rebase_rebasing_wcp=rebasingwcp)
972 if rbsrt.inmemory and rebasingwcp:
962 if rbsrt.inmemory and rebasingwcp:
973 rbsrt.inmemory = False
974 # Check these since we did not before.
963 # Check these since we did not before.
975 cmdutil.checkunfinished(repo)
964 cmdutil.checkunfinished(repo)
976 cmdutil.bailifchanged(repo)
965 cmdutil.bailifchanged(repo)
977
966
978 if not destf:
967 if not destf:
979 dest = repo[_destrebase(repo, rebaseset, destspace=destspace)]
968 dest = repo[_destrebase(repo, rebaseset, destspace=destspace)]
980 destf = bytes(dest)
969 destf = bytes(dest)
981
970
982 allsrc = revsetlang.formatspec('%ld', rebaseset)
971 allsrc = revsetlang.formatspec('%ld', rebaseset)
983 alias = {'ALLSRC': allsrc}
972 alias = {'ALLSRC': allsrc}
984
973
985 if dest is None:
974 if dest is None:
986 try:
975 try:
987 # fast path: try to resolve dest without SRC alias
976 # fast path: try to resolve dest without SRC alias
988 dest = scmutil.revsingle(repo, destf, localalias=alias)
977 dest = scmutil.revsingle(repo, destf, localalias=alias)
989 except error.RepoLookupError:
978 except error.RepoLookupError:
990 # multi-dest path: resolve dest for each SRC separately
979 # multi-dest path: resolve dest for each SRC separately
991 destmap = {}
980 destmap = {}
992 for r in rebaseset:
981 for r in rebaseset:
993 alias['SRC'] = revsetlang.formatspec('%d', r)
982 alias['SRC'] = revsetlang.formatspec('%d', r)
994 # use repo.anyrevs instead of scmutil.revsingle because we
983 # use repo.anyrevs instead of scmutil.revsingle because we
995 # don't want to abort if destset is empty.
984 # don't want to abort if destset is empty.
996 destset = repo.anyrevs([destf], user=True, localalias=alias)
985 destset = repo.anyrevs([destf], user=True, localalias=alias)
997 size = len(destset)
986 size = len(destset)
998 if size == 1:
987 if size == 1:
999 destmap[r] = destset.first()
988 destmap[r] = destset.first()
1000 elif size == 0:
989 elif size == 0:
1001 ui.note(_('skipping %s - empty destination\n') % repo[r])
990 ui.note(_('skipping %s - empty destination\n') % repo[r])
1002 else:
991 else:
1003 raise error.Abort(_('rebase destination for %s is not '
992 raise error.Abort(_('rebase destination for %s is not '
1004 'unique') % repo[r])
993 'unique') % repo[r])
1005
994
1006 if dest is not None:
995 if dest is not None:
1007 # single-dest case: assign dest to each rev in rebaseset
996 # single-dest case: assign dest to each rev in rebaseset
1008 destrev = dest.rev()
997 destrev = dest.rev()
1009 destmap = {r: destrev for r in rebaseset} # {srcrev: destrev}
998 destmap = {r: destrev for r in rebaseset} # {srcrev: destrev}
1010
999
1011 if not destmap:
1000 if not destmap:
1012 ui.status(_('nothing to rebase - empty destination\n'))
1001 ui.status(_('nothing to rebase - empty destination\n'))
1013 return None
1002 return None
1014
1003
1015 return destmap
1004 return destmap
1016
1005
1017 def externalparent(repo, state, destancestors):
1006 def externalparent(repo, state, destancestors):
1018 """Return the revision that should be used as the second parent
1007 """Return the revision that should be used as the second parent
1019 when the revisions in state is collapsed on top of destancestors.
1008 when the revisions in state is collapsed on top of destancestors.
1020 Abort if there is more than one parent.
1009 Abort if there is more than one parent.
1021 """
1010 """
1022 parents = set()
1011 parents = set()
1023 source = min(state)
1012 source = min(state)
1024 for rev in state:
1013 for rev in state:
1025 if rev == source:
1014 if rev == source:
1026 continue
1015 continue
1027 for p in repo[rev].parents():
1016 for p in repo[rev].parents():
1028 if (p.rev() not in state
1017 if (p.rev() not in state
1029 and p.rev() not in destancestors):
1018 and p.rev() not in destancestors):
1030 parents.add(p.rev())
1019 parents.add(p.rev())
1031 if not parents:
1020 if not parents:
1032 return nullrev
1021 return nullrev
1033 if len(parents) == 1:
1022 if len(parents) == 1:
1034 return parents.pop()
1023 return parents.pop()
1035 raise error.Abort(_('unable to collapse on top of %d, there is more '
1024 raise error.Abort(_('unable to collapse on top of %d, there is more '
1036 'than one external parent: %s') %
1025 'than one external parent: %s') %
1037 (max(destancestors),
1026 (max(destancestors),
1038 ', '.join("%d" % p for p in sorted(parents))))
1027 ', '.join("%d" % p for p in sorted(parents))))
1039
1028
1040 def concludememorynode(repo, rev, p1, p2, wctx=None,
1029 def concludememorynode(repo, rev, p1, p2, wctx=None,
1041 commitmsg=None, editor=None, extrafn=None,
1030 commitmsg=None, editor=None, extrafn=None,
1042 keepbranches=False, date=None):
1031 keepbranches=False, date=None):
1043 '''Commit the memory changes with parents p1 and p2. Reuse commit info from
1032 '''Commit the memory changes with parents p1 and p2. Reuse commit info from
1044 rev but also store useful information in extra.
1033 rev but also store useful information in extra.
1045 Return node of committed revision.'''
1034 Return node of committed revision.'''
1046 ctx = repo[rev]
1035 ctx = repo[rev]
1047 if commitmsg is None:
1036 if commitmsg is None:
1048 commitmsg = ctx.description()
1037 commitmsg = ctx.description()
1049 keepbranch = keepbranches and repo[p1].branch() != ctx.branch()
1038 keepbranch = keepbranches and repo[p1].branch() != ctx.branch()
1050 extra = {'rebase_source': ctx.hex()}
1039 extra = {'rebase_source': ctx.hex()}
1051 if extrafn:
1040 if extrafn:
1052 extrafn(ctx, extra)
1041 extrafn(ctx, extra)
1053
1042
1054 destphase = max(ctx.phase(), phases.draft)
1043 destphase = max(ctx.phase(), phases.draft)
1055 overrides = {('phases', 'new-commit'): destphase}
1044 overrides = {('phases', 'new-commit'): destphase}
1056 if keepbranch:
1045 if keepbranch:
1057 overrides[('ui', 'allowemptycommit')] = True
1046 overrides[('ui', 'allowemptycommit')] = True
1058 with repo.ui.configoverride(overrides, 'rebase'):
1047 with repo.ui.configoverride(overrides, 'rebase'):
1059 # Replicates the empty check in ``repo.commit``.
1048 # Replicates the empty check in ``repo.commit``.
1060 if wctx.isempty() and not repo.ui.configbool('ui', 'allowemptycommit'):
1049 if wctx.isempty() and not repo.ui.configbool('ui', 'allowemptycommit'):
1061 return None
1050 return None
1062
1051
1063 if date is None:
1052 if date is None:
1064 date = ctx.date()
1053 date = ctx.date()
1065
1054
1066 # By convention, ``extra['branch']`` (set by extrafn) clobbers
1055 # By convention, ``extra['branch']`` (set by extrafn) clobbers
1067 # ``branch`` (used when passing ``--keepbranches``).
1056 # ``branch`` (used when passing ``--keepbranches``).
1068 branch = repo[p1].branch()
1057 branch = repo[p1].branch()
1069 if 'branch' in extra:
1058 if 'branch' in extra:
1070 branch = extra['branch']
1059 branch = extra['branch']
1071
1060
1072 memctx = wctx.tomemctx(commitmsg, parents=(p1, p2), date=date,
1061 memctx = wctx.tomemctx(commitmsg, parents=(p1, p2), date=date,
1073 extra=extra, user=ctx.user(), branch=branch, editor=editor)
1062 extra=extra, user=ctx.user(), branch=branch, editor=editor)
1074 commitres = repo.commitctx(memctx)
1063 commitres = repo.commitctx(memctx)
1075 wctx.clean() # Might be reused
1064 wctx.clean() # Might be reused
1076 return commitres
1065 return commitres
1077
1066
1078 def concludenode(repo, rev, p1, p2, commitmsg=None, editor=None, extrafn=None,
1067 def concludenode(repo, rev, p1, p2, commitmsg=None, editor=None, extrafn=None,
1079 keepbranches=False, date=None):
1068 keepbranches=False, date=None):
1080 '''Commit the wd changes with parents p1 and p2. Reuse commit info from rev
1069 '''Commit the wd changes with parents p1 and p2. Reuse commit info from rev
1081 but also store useful information in extra.
1070 but also store useful information in extra.
1082 Return node of committed revision.'''
1071 Return node of committed revision.'''
1083 dsguard = util.nullcontextmanager()
1072 dsguard = util.nullcontextmanager()
1084 if not repo.ui.configbool('rebase', 'singletransaction'):
1073 if not repo.ui.configbool('rebase', 'singletransaction'):
1085 dsguard = dirstateguard.dirstateguard(repo, 'rebase')
1074 dsguard = dirstateguard.dirstateguard(repo, 'rebase')
1086 with dsguard:
1075 with dsguard:
1087 repo.setparents(repo[p1].node(), repo[p2].node())
1076 repo.setparents(repo[p1].node(), repo[p2].node())
1088 ctx = repo[rev]
1077 ctx = repo[rev]
1089 if commitmsg is None:
1078 if commitmsg is None:
1090 commitmsg = ctx.description()
1079 commitmsg = ctx.description()
1091 keepbranch = keepbranches and repo[p1].branch() != ctx.branch()
1080 keepbranch = keepbranches and repo[p1].branch() != ctx.branch()
1092 extra = {'rebase_source': ctx.hex()}
1081 extra = {'rebase_source': ctx.hex()}
1093 if extrafn:
1082 if extrafn:
1094 extrafn(ctx, extra)
1083 extrafn(ctx, extra)
1095
1084
1096 destphase = max(ctx.phase(), phases.draft)
1085 destphase = max(ctx.phase(), phases.draft)
1097 overrides = {('phases', 'new-commit'): destphase}
1086 overrides = {('phases', 'new-commit'): destphase}
1098 if keepbranch:
1087 if keepbranch:
1099 overrides[('ui', 'allowemptycommit')] = True
1088 overrides[('ui', 'allowemptycommit')] = True
1100 with repo.ui.configoverride(overrides, 'rebase'):
1089 with repo.ui.configoverride(overrides, 'rebase'):
1101 # Commit might fail if unresolved files exist
1090 # Commit might fail if unresolved files exist
1102 if date is None:
1091 if date is None:
1103 date = ctx.date()
1092 date = ctx.date()
1104 newnode = repo.commit(text=commitmsg, user=ctx.user(),
1093 newnode = repo.commit(text=commitmsg, user=ctx.user(),
1105 date=date, extra=extra, editor=editor)
1094 date=date, extra=extra, editor=editor)
1106
1095
1107 repo.dirstate.setbranch(repo[newnode].branch())
1096 repo.dirstate.setbranch(repo[newnode].branch())
1108 return newnode
1097 return newnode
1109
1098
1110 def rebasenode(repo, rev, p1, base, collapse, dest, wctx):
1099 def rebasenode(repo, rev, p1, base, collapse, dest, wctx):
1111 'Rebase a single revision rev on top of p1 using base as merge ancestor'
1100 'Rebase a single revision rev on top of p1 using base as merge ancestor'
1112 # Merge phase
1101 # Merge phase
1113 # Update to destination and merge it with local
1102 # Update to destination and merge it with local
1114 if wctx.isinmemory():
1103 if wctx.isinmemory():
1115 wctx.setbase(repo[p1])
1104 wctx.setbase(repo[p1])
1116 else:
1105 else:
1117 if repo['.'].rev() != p1:
1106 if repo['.'].rev() != p1:
1118 repo.ui.debug(" update to %d:%s\n" % (p1, repo[p1]))
1107 repo.ui.debug(" update to %d:%s\n" % (p1, repo[p1]))
1119 mergemod.update(repo, p1, False, True)
1108 mergemod.update(repo, p1, False, True)
1120 else:
1109 else:
1121 repo.ui.debug(" already in destination\n")
1110 repo.ui.debug(" already in destination\n")
1122 # This is, alas, necessary to invalidate workingctx's manifest cache,
1111 # This is, alas, necessary to invalidate workingctx's manifest cache,
1123 # as well as other data we litter on it in other places.
1112 # as well as other data we litter on it in other places.
1124 wctx = repo[None]
1113 wctx = repo[None]
1125 repo.dirstate.write(repo.currenttransaction())
1114 repo.dirstate.write(repo.currenttransaction())
1126 repo.ui.debug(" merge against %d:%s\n" % (rev, repo[rev]))
1115 repo.ui.debug(" merge against %d:%s\n" % (rev, repo[rev]))
1127 if base is not None:
1116 if base is not None:
1128 repo.ui.debug(" detach base %d:%s\n" % (base, repo[base]))
1117 repo.ui.debug(" detach base %d:%s\n" % (base, repo[base]))
1129 # When collapsing in-place, the parent is the common ancestor, we
1118 # When collapsing in-place, the parent is the common ancestor, we
1130 # have to allow merging with it.
1119 # have to allow merging with it.
1131 stats = mergemod.update(repo, rev, True, True, base, collapse,
1120 stats = mergemod.update(repo, rev, True, True, base, collapse,
1132 labels=['dest', 'source'], wc=wctx)
1121 labels=['dest', 'source'], wc=wctx)
1133 if collapse:
1122 if collapse:
1134 copies.duplicatecopies(repo, wctx, rev, dest)
1123 copies.duplicatecopies(repo, wctx, rev, dest)
1135 else:
1124 else:
1136 # If we're not using --collapse, we need to
1125 # If we're not using --collapse, we need to
1137 # duplicate copies between the revision we're
1126 # duplicate copies between the revision we're
1138 # rebasing and its first parent, but *not*
1127 # rebasing and its first parent, but *not*
1139 # duplicate any copies that have already been
1128 # duplicate any copies that have already been
1140 # performed in the destination.
1129 # performed in the destination.
1141 p1rev = repo[rev].p1().rev()
1130 p1rev = repo[rev].p1().rev()
1142 copies.duplicatecopies(repo, wctx, rev, p1rev, skiprev=dest)
1131 copies.duplicatecopies(repo, wctx, rev, p1rev, skiprev=dest)
1143 return stats
1132 return stats
1144
1133
1145 def adjustdest(repo, rev, destmap, state, skipped):
1134 def adjustdest(repo, rev, destmap, state, skipped):
1146 """adjust rebase destination given the current rebase state
1135 """adjust rebase destination given the current rebase state
1147
1136
1148 rev is what is being rebased. Return a list of two revs, which are the
1137 rev is what is being rebased. Return a list of two revs, which are the
1149 adjusted destinations for rev's p1 and p2, respectively. If a parent is
1138 adjusted destinations for rev's p1 and p2, respectively. If a parent is
1150 nullrev, return dest without adjustment for it.
1139 nullrev, return dest without adjustment for it.
1151
1140
1152 For example, when doing rebasing B+E to F, C to G, rebase will first move B
1141 For example, when doing rebasing B+E to F, C to G, rebase will first move B
1153 to B1, and E's destination will be adjusted from F to B1.
1142 to B1, and E's destination will be adjusted from F to B1.
1154
1143
1155 B1 <- written during rebasing B
1144 B1 <- written during rebasing B
1156 |
1145 |
1157 F <- original destination of B, E
1146 F <- original destination of B, E
1158 |
1147 |
1159 | E <- rev, which is being rebased
1148 | E <- rev, which is being rebased
1160 | |
1149 | |
1161 | D <- prev, one parent of rev being checked
1150 | D <- prev, one parent of rev being checked
1162 | |
1151 | |
1163 | x <- skipped, ex. no successor or successor in (::dest)
1152 | x <- skipped, ex. no successor or successor in (::dest)
1164 | |
1153 | |
1165 | C <- rebased as C', different destination
1154 | C <- rebased as C', different destination
1166 | |
1155 | |
1167 | B <- rebased as B1 C'
1156 | B <- rebased as B1 C'
1168 |/ |
1157 |/ |
1169 A G <- destination of C, different
1158 A G <- destination of C, different
1170
1159
1171 Another example about merge changeset, rebase -r C+G+H -d K, rebase will
1160 Another example about merge changeset, rebase -r C+G+H -d K, rebase will
1172 first move C to C1, G to G1, and when it's checking H, the adjusted
1161 first move C to C1, G to G1, and when it's checking H, the adjusted
1173 destinations will be [C1, G1].
1162 destinations will be [C1, G1].
1174
1163
1175 H C1 G1
1164 H C1 G1
1176 /| | /
1165 /| | /
1177 F G |/
1166 F G |/
1178 K | | -> K
1167 K | | -> K
1179 | C D |
1168 | C D |
1180 | |/ |
1169 | |/ |
1181 | B | ...
1170 | B | ...
1182 |/ |/
1171 |/ |/
1183 A A
1172 A A
1184
1173
1185 Besides, adjust dest according to existing rebase information. For example,
1174 Besides, adjust dest according to existing rebase information. For example,
1186
1175
1187 B C D B needs to be rebased on top of C, C needs to be rebased on top
1176 B C D B needs to be rebased on top of C, C needs to be rebased on top
1188 \|/ of D. We will rebase C first.
1177 \|/ of D. We will rebase C first.
1189 A
1178 A
1190
1179
1191 C' After rebasing C, when considering B's destination, use C'
1180 C' After rebasing C, when considering B's destination, use C'
1192 | instead of the original C.
1181 | instead of the original C.
1193 B D
1182 B D
1194 \ /
1183 \ /
1195 A
1184 A
1196 """
1185 """
1197 # pick already rebased revs with same dest from state as interesting source
1186 # pick already rebased revs with same dest from state as interesting source
1198 dest = destmap[rev]
1187 dest = destmap[rev]
1199 source = [s for s, d in state.items()
1188 source = [s for s, d in state.items()
1200 if d > 0 and destmap[s] == dest and s not in skipped]
1189 if d > 0 and destmap[s] == dest and s not in skipped]
1201
1190
1202 result = []
1191 result = []
1203 for prev in repo.changelog.parentrevs(rev):
1192 for prev in repo.changelog.parentrevs(rev):
1204 adjusted = dest
1193 adjusted = dest
1205 if prev != nullrev:
1194 if prev != nullrev:
1206 candidate = repo.revs('max(%ld and (::%d))', source, prev).first()
1195 candidate = repo.revs('max(%ld and (::%d))', source, prev).first()
1207 if candidate is not None:
1196 if candidate is not None:
1208 adjusted = state[candidate]
1197 adjusted = state[candidate]
1209 if adjusted == dest and dest in state:
1198 if adjusted == dest and dest in state:
1210 adjusted = state[dest]
1199 adjusted = state[dest]
1211 if adjusted == revtodo:
1200 if adjusted == revtodo:
1212 # sortsource should produce an order that makes this impossible
1201 # sortsource should produce an order that makes this impossible
1213 raise error.ProgrammingError(
1202 raise error.ProgrammingError(
1214 'rev %d should be rebased already at this time' % dest)
1203 'rev %d should be rebased already at this time' % dest)
1215 result.append(adjusted)
1204 result.append(adjusted)
1216 return result
1205 return result
1217
1206
1218 def _checkobsrebase(repo, ui, rebaseobsrevs, rebaseobsskipped):
1207 def _checkobsrebase(repo, ui, rebaseobsrevs, rebaseobsskipped):
1219 """
1208 """
1220 Abort if rebase will create divergence or rebase is noop because of markers
1209 Abort if rebase will create divergence or rebase is noop because of markers
1221
1210
1222 `rebaseobsrevs`: set of obsolete revision in source
1211 `rebaseobsrevs`: set of obsolete revision in source
1223 `rebaseobsskipped`: set of revisions from source skipped because they have
1212 `rebaseobsskipped`: set of revisions from source skipped because they have
1224 successors in destination or no non-obsolete successor.
1213 successors in destination or no non-obsolete successor.
1225 """
1214 """
1226 # Obsolete node with successors not in dest leads to divergence
1215 # Obsolete node with successors not in dest leads to divergence
1227 divergenceok = ui.configbool('experimental',
1216 divergenceok = ui.configbool('experimental',
1228 'evolution.allowdivergence')
1217 'evolution.allowdivergence')
1229 divergencebasecandidates = rebaseobsrevs - rebaseobsskipped
1218 divergencebasecandidates = rebaseobsrevs - rebaseobsskipped
1230
1219
1231 if divergencebasecandidates and not divergenceok:
1220 if divergencebasecandidates and not divergenceok:
1232 divhashes = (bytes(repo[r])
1221 divhashes = (bytes(repo[r])
1233 for r in divergencebasecandidates)
1222 for r in divergencebasecandidates)
1234 msg = _("this rebase will cause "
1223 msg = _("this rebase will cause "
1235 "divergences from: %s")
1224 "divergences from: %s")
1236 h = _("to force the rebase please set "
1225 h = _("to force the rebase please set "
1237 "experimental.evolution.allowdivergence=True")
1226 "experimental.evolution.allowdivergence=True")
1238 raise error.Abort(msg % (",".join(divhashes),), hint=h)
1227 raise error.Abort(msg % (",".join(divhashes),), hint=h)
1239
1228
1240 def successorrevs(unfi, rev):
1229 def successorrevs(unfi, rev):
1241 """yield revision numbers for successors of rev"""
1230 """yield revision numbers for successors of rev"""
1242 assert unfi.filtername is None
1231 assert unfi.filtername is None
1243 nodemap = unfi.changelog.nodemap
1232 nodemap = unfi.changelog.nodemap
1244 for s in obsutil.allsuccessors(unfi.obsstore, [unfi[rev].node()]):
1233 for s in obsutil.allsuccessors(unfi.obsstore, [unfi[rev].node()]):
1245 if s in nodemap:
1234 if s in nodemap:
1246 yield nodemap[s]
1235 yield nodemap[s]
1247
1236
1248 def defineparents(repo, rev, destmap, state, skipped, obsskipped):
1237 def defineparents(repo, rev, destmap, state, skipped, obsskipped):
1249 """Return new parents and optionally a merge base for rev being rebased
1238 """Return new parents and optionally a merge base for rev being rebased
1250
1239
1251 The destination specified by "dest" cannot always be used directly because
1240 The destination specified by "dest" cannot always be used directly because
1252 previously rebase result could affect destination. For example,
1241 previously rebase result could affect destination. For example,
1253
1242
1254 D E rebase -r C+D+E -d B
1243 D E rebase -r C+D+E -d B
1255 |/ C will be rebased to C'
1244 |/ C will be rebased to C'
1256 B C D's new destination will be C' instead of B
1245 B C D's new destination will be C' instead of B
1257 |/ E's new destination will be C' instead of B
1246 |/ E's new destination will be C' instead of B
1258 A
1247 A
1259
1248
1260 The new parents of a merge is slightly more complicated. See the comment
1249 The new parents of a merge is slightly more complicated. See the comment
1261 block below.
1250 block below.
1262 """
1251 """
1263 # use unfiltered changelog since successorrevs may return filtered nodes
1252 # use unfiltered changelog since successorrevs may return filtered nodes
1264 assert repo.filtername is None
1253 assert repo.filtername is None
1265 cl = repo.changelog
1254 cl = repo.changelog
1266 def isancestor(a, b):
1255 def isancestor(a, b):
1267 # take revision numbers instead of nodes
1256 # take revision numbers instead of nodes
1268 if a == b:
1257 if a == b:
1269 return True
1258 return True
1270 elif a > b:
1259 elif a > b:
1271 return False
1260 return False
1272 return cl.isancestor(cl.node(a), cl.node(b))
1261 return cl.isancestor(cl.node(a), cl.node(b))
1273
1262
1274 dest = destmap[rev]
1263 dest = destmap[rev]
1275 oldps = repo.changelog.parentrevs(rev) # old parents
1264 oldps = repo.changelog.parentrevs(rev) # old parents
1276 newps = [nullrev, nullrev] # new parents
1265 newps = [nullrev, nullrev] # new parents
1277 dests = adjustdest(repo, rev, destmap, state, skipped)
1266 dests = adjustdest(repo, rev, destmap, state, skipped)
1278 bases = list(oldps) # merge base candidates, initially just old parents
1267 bases = list(oldps) # merge base candidates, initially just old parents
1279
1268
1280 if all(r == nullrev for r in oldps[1:]):
1269 if all(r == nullrev for r in oldps[1:]):
1281 # For non-merge changeset, just move p to adjusted dest as requested.
1270 # For non-merge changeset, just move p to adjusted dest as requested.
1282 newps[0] = dests[0]
1271 newps[0] = dests[0]
1283 else:
1272 else:
1284 # For merge changeset, if we move p to dests[i] unconditionally, both
1273 # For merge changeset, if we move p to dests[i] unconditionally, both
1285 # parents may change and the end result looks like "the merge loses a
1274 # parents may change and the end result looks like "the merge loses a
1286 # parent", which is a surprise. This is a limit because "--dest" only
1275 # parent", which is a surprise. This is a limit because "--dest" only
1287 # accepts one dest per src.
1276 # accepts one dest per src.
1288 #
1277 #
1289 # Therefore, only move p with reasonable conditions (in this order):
1278 # Therefore, only move p with reasonable conditions (in this order):
1290 # 1. use dest, if dest is a descendent of (p or one of p's successors)
1279 # 1. use dest, if dest is a descendent of (p or one of p's successors)
1291 # 2. use p's rebased result, if p is rebased (state[p] > 0)
1280 # 2. use p's rebased result, if p is rebased (state[p] > 0)
1292 #
1281 #
1293 # Comparing with adjustdest, the logic here does some additional work:
1282 # Comparing with adjustdest, the logic here does some additional work:
1294 # 1. decide which parents will not be moved towards dest
1283 # 1. decide which parents will not be moved towards dest
1295 # 2. if the above decision is "no", should a parent still be moved
1284 # 2. if the above decision is "no", should a parent still be moved
1296 # because it was rebased?
1285 # because it was rebased?
1297 #
1286 #
1298 # For example:
1287 # For example:
1299 #
1288 #
1300 # C # "rebase -r C -d D" is an error since none of the parents
1289 # C # "rebase -r C -d D" is an error since none of the parents
1301 # /| # can be moved. "rebase -r B+C -d D" will move C's parent
1290 # /| # can be moved. "rebase -r B+C -d D" will move C's parent
1302 # A B D # B (using rule "2."), since B will be rebased.
1291 # A B D # B (using rule "2."), since B will be rebased.
1303 #
1292 #
1304 # The loop tries to be not rely on the fact that a Mercurial node has
1293 # The loop tries to be not rely on the fact that a Mercurial node has
1305 # at most 2 parents.
1294 # at most 2 parents.
1306 for i, p in enumerate(oldps):
1295 for i, p in enumerate(oldps):
1307 np = p # new parent
1296 np = p # new parent
1308 if any(isancestor(x, dests[i]) for x in successorrevs(repo, p)):
1297 if any(isancestor(x, dests[i]) for x in successorrevs(repo, p)):
1309 np = dests[i]
1298 np = dests[i]
1310 elif p in state and state[p] > 0:
1299 elif p in state and state[p] > 0:
1311 np = state[p]
1300 np = state[p]
1312
1301
1313 # "bases" only record "special" merge bases that cannot be
1302 # "bases" only record "special" merge bases that cannot be
1314 # calculated from changelog DAG (i.e. isancestor(p, np) is False).
1303 # calculated from changelog DAG (i.e. isancestor(p, np) is False).
1315 # For example:
1304 # For example:
1316 #
1305 #
1317 # B' # rebase -s B -d D, when B was rebased to B'. dest for C
1306 # B' # rebase -s B -d D, when B was rebased to B'. dest for C
1318 # | C # is B', but merge base for C is B, instead of
1307 # | C # is B', but merge base for C is B, instead of
1319 # D | # changelog.ancestor(C, B') == A. If changelog DAG and
1308 # D | # changelog.ancestor(C, B') == A. If changelog DAG and
1320 # | B # "state" edges are merged (so there will be an edge from
1309 # | B # "state" edges are merged (so there will be an edge from
1321 # |/ # B to B'), the merge base is still ancestor(C, B') in
1310 # |/ # B to B'), the merge base is still ancestor(C, B') in
1322 # A # the merged graph.
1311 # A # the merged graph.
1323 #
1312 #
1324 # Also see https://bz.mercurial-scm.org/show_bug.cgi?id=1950#c8
1313 # Also see https://bz.mercurial-scm.org/show_bug.cgi?id=1950#c8
1325 # which uses "virtual null merge" to explain this situation.
1314 # which uses "virtual null merge" to explain this situation.
1326 if isancestor(p, np):
1315 if isancestor(p, np):
1327 bases[i] = nullrev
1316 bases[i] = nullrev
1328
1317
1329 # If one parent becomes an ancestor of the other, drop the ancestor
1318 # If one parent becomes an ancestor of the other, drop the ancestor
1330 for j, x in enumerate(newps[:i]):
1319 for j, x in enumerate(newps[:i]):
1331 if x == nullrev:
1320 if x == nullrev:
1332 continue
1321 continue
1333 if isancestor(np, x): # CASE-1
1322 if isancestor(np, x): # CASE-1
1334 np = nullrev
1323 np = nullrev
1335 elif isancestor(x, np): # CASE-2
1324 elif isancestor(x, np): # CASE-2
1336 newps[j] = np
1325 newps[j] = np
1337 np = nullrev
1326 np = nullrev
1338 # New parents forming an ancestor relationship does not
1327 # New parents forming an ancestor relationship does not
1339 # mean the old parents have a similar relationship. Do not
1328 # mean the old parents have a similar relationship. Do not
1340 # set bases[x] to nullrev.
1329 # set bases[x] to nullrev.
1341 bases[j], bases[i] = bases[i], bases[j]
1330 bases[j], bases[i] = bases[i], bases[j]
1342
1331
1343 newps[i] = np
1332 newps[i] = np
1344
1333
1345 # "rebasenode" updates to new p1, and the old p1 will be used as merge
1334 # "rebasenode" updates to new p1, and the old p1 will be used as merge
1346 # base. If only p2 changes, merging using unchanged p1 as merge base is
1335 # base. If only p2 changes, merging using unchanged p1 as merge base is
1347 # suboptimal. Therefore swap parents to make the merge sane.
1336 # suboptimal. Therefore swap parents to make the merge sane.
1348 if newps[1] != nullrev and oldps[0] == newps[0]:
1337 if newps[1] != nullrev and oldps[0] == newps[0]:
1349 assert len(newps) == 2 and len(oldps) == 2
1338 assert len(newps) == 2 and len(oldps) == 2
1350 newps.reverse()
1339 newps.reverse()
1351 bases.reverse()
1340 bases.reverse()
1352
1341
1353 # No parent change might be an error because we fail to make rev a
1342 # No parent change might be an error because we fail to make rev a
1354 # descendent of requested dest. This can happen, for example:
1343 # descendent of requested dest. This can happen, for example:
1355 #
1344 #
1356 # C # rebase -r C -d D
1345 # C # rebase -r C -d D
1357 # /| # None of A and B will be changed to D and rebase fails.
1346 # /| # None of A and B will be changed to D and rebase fails.
1358 # A B D
1347 # A B D
1359 if set(newps) == set(oldps) and dest not in newps:
1348 if set(newps) == set(oldps) and dest not in newps:
1360 raise error.Abort(_('cannot rebase %d:%s without '
1349 raise error.Abort(_('cannot rebase %d:%s without '
1361 'moving at least one of its parents')
1350 'moving at least one of its parents')
1362 % (rev, repo[rev]))
1351 % (rev, repo[rev]))
1363
1352
1364 # Source should not be ancestor of dest. The check here guarantees it's
1353 # Source should not be ancestor of dest. The check here guarantees it's
1365 # impossible. With multi-dest, the initial check does not cover complex
1354 # impossible. With multi-dest, the initial check does not cover complex
1366 # cases since we don't have abstractions to dry-run rebase cheaply.
1355 # cases since we don't have abstractions to dry-run rebase cheaply.
1367 if any(p != nullrev and isancestor(rev, p) for p in newps):
1356 if any(p != nullrev and isancestor(rev, p) for p in newps):
1368 raise error.Abort(_('source is ancestor of destination'))
1357 raise error.Abort(_('source is ancestor of destination'))
1369
1358
1370 # "rebasenode" updates to new p1, use the corresponding merge base.
1359 # "rebasenode" updates to new p1, use the corresponding merge base.
1371 if bases[0] != nullrev:
1360 if bases[0] != nullrev:
1372 base = bases[0]
1361 base = bases[0]
1373 else:
1362 else:
1374 base = None
1363 base = None
1375
1364
1376 # Check if the merge will contain unwanted changes. That may happen if
1365 # Check if the merge will contain unwanted changes. That may happen if
1377 # there are multiple special (non-changelog ancestor) merge bases, which
1366 # there are multiple special (non-changelog ancestor) merge bases, which
1378 # cannot be handled well by the 3-way merge algorithm. For example:
1367 # cannot be handled well by the 3-way merge algorithm. For example:
1379 #
1368 #
1380 # F
1369 # F
1381 # /|
1370 # /|
1382 # D E # "rebase -r D+E+F -d Z", when rebasing F, if "D" was chosen
1371 # D E # "rebase -r D+E+F -d Z", when rebasing F, if "D" was chosen
1383 # | | # as merge base, the difference between D and F will include
1372 # | | # as merge base, the difference between D and F will include
1384 # B C # C, so the rebased F will contain C surprisingly. If "E" was
1373 # B C # C, so the rebased F will contain C surprisingly. If "E" was
1385 # |/ # chosen, the rebased F will contain B.
1374 # |/ # chosen, the rebased F will contain B.
1386 # A Z
1375 # A Z
1387 #
1376 #
1388 # But our merge base candidates (D and E in above case) could still be
1377 # But our merge base candidates (D and E in above case) could still be
1389 # better than the default (ancestor(F, Z) == null). Therefore still
1378 # better than the default (ancestor(F, Z) == null). Therefore still
1390 # pick one (so choose p1 above).
1379 # pick one (so choose p1 above).
1391 if sum(1 for b in bases if b != nullrev) > 1:
1380 if sum(1 for b in bases if b != nullrev) > 1:
1392 unwanted = [None, None] # unwanted[i]: unwanted revs if choose bases[i]
1381 unwanted = [None, None] # unwanted[i]: unwanted revs if choose bases[i]
1393 for i, base in enumerate(bases):
1382 for i, base in enumerate(bases):
1394 if base == nullrev:
1383 if base == nullrev:
1395 continue
1384 continue
1396 # Revisions in the side (not chosen as merge base) branch that
1385 # Revisions in the side (not chosen as merge base) branch that
1397 # might contain "surprising" contents
1386 # might contain "surprising" contents
1398 siderevs = list(repo.revs('((%ld-%d) %% (%d+%d))',
1387 siderevs = list(repo.revs('((%ld-%d) %% (%d+%d))',
1399 bases, base, base, dest))
1388 bases, base, base, dest))
1400
1389
1401 # If those revisions are covered by rebaseset, the result is good.
1390 # If those revisions are covered by rebaseset, the result is good.
1402 # A merge in rebaseset would be considered to cover its ancestors.
1391 # A merge in rebaseset would be considered to cover its ancestors.
1403 if siderevs:
1392 if siderevs:
1404 rebaseset = [r for r, d in state.items()
1393 rebaseset = [r for r, d in state.items()
1405 if d > 0 and r not in obsskipped]
1394 if d > 0 and r not in obsskipped]
1406 merges = [r for r in rebaseset
1395 merges = [r for r in rebaseset
1407 if cl.parentrevs(r)[1] != nullrev]
1396 if cl.parentrevs(r)[1] != nullrev]
1408 unwanted[i] = list(repo.revs('%ld - (::%ld) - %ld',
1397 unwanted[i] = list(repo.revs('%ld - (::%ld) - %ld',
1409 siderevs, merges, rebaseset))
1398 siderevs, merges, rebaseset))
1410
1399
1411 # Choose a merge base that has a minimal number of unwanted revs.
1400 # Choose a merge base that has a minimal number of unwanted revs.
1412 l, i = min((len(revs), i)
1401 l, i = min((len(revs), i)
1413 for i, revs in enumerate(unwanted) if revs is not None)
1402 for i, revs in enumerate(unwanted) if revs is not None)
1414 base = bases[i]
1403 base = bases[i]
1415
1404
1416 # newps[0] should match merge base if possible. Currently, if newps[i]
1405 # newps[0] should match merge base if possible. Currently, if newps[i]
1417 # is nullrev, the only case is newps[i] and newps[j] (j < i), one is
1406 # is nullrev, the only case is newps[i] and newps[j] (j < i), one is
1418 # the other's ancestor. In that case, it's fine to not swap newps here.
1407 # the other's ancestor. In that case, it's fine to not swap newps here.
1419 # (see CASE-1 and CASE-2 above)
1408 # (see CASE-1 and CASE-2 above)
1420 if i != 0 and newps[i] != nullrev:
1409 if i != 0 and newps[i] != nullrev:
1421 newps[0], newps[i] = newps[i], newps[0]
1410 newps[0], newps[i] = newps[i], newps[0]
1422
1411
1423 # The merge will include unwanted revisions. Abort now. Revisit this if
1412 # The merge will include unwanted revisions. Abort now. Revisit this if
1424 # we have a more advanced merge algorithm that handles multiple bases.
1413 # we have a more advanced merge algorithm that handles multiple bases.
1425 if l > 0:
1414 if l > 0:
1426 unwanteddesc = _(' or ').join(
1415 unwanteddesc = _(' or ').join(
1427 (', '.join('%d:%s' % (r, repo[r]) for r in revs)
1416 (', '.join('%d:%s' % (r, repo[r]) for r in revs)
1428 for revs in unwanted if revs is not None))
1417 for revs in unwanted if revs is not None))
1429 raise error.Abort(
1418 raise error.Abort(
1430 _('rebasing %d:%s will include unwanted changes from %s')
1419 _('rebasing %d:%s will include unwanted changes from %s')
1431 % (rev, repo[rev], unwanteddesc))
1420 % (rev, repo[rev], unwanteddesc))
1432
1421
1433 repo.ui.debug(" future parents are %d and %d\n" % tuple(newps))
1422 repo.ui.debug(" future parents are %d and %d\n" % tuple(newps))
1434
1423
1435 return newps[0], newps[1], base
1424 return newps[0], newps[1], base
1436
1425
1437 def isagitpatch(repo, patchname):
1426 def isagitpatch(repo, patchname):
1438 'Return true if the given patch is in git format'
1427 'Return true if the given patch is in git format'
1439 mqpatch = os.path.join(repo.mq.path, patchname)
1428 mqpatch = os.path.join(repo.mq.path, patchname)
1440 for line in patch.linereader(open(mqpatch, 'rb')):
1429 for line in patch.linereader(open(mqpatch, 'rb')):
1441 if line.startswith('diff --git'):
1430 if line.startswith('diff --git'):
1442 return True
1431 return True
1443 return False
1432 return False
1444
1433
1445 def updatemq(repo, state, skipped, **opts):
1434 def updatemq(repo, state, skipped, **opts):
1446 'Update rebased mq patches - finalize and then import them'
1435 'Update rebased mq patches - finalize and then import them'
1447 mqrebase = {}
1436 mqrebase = {}
1448 mq = repo.mq
1437 mq = repo.mq
1449 original_series = mq.fullseries[:]
1438 original_series = mq.fullseries[:]
1450 skippedpatches = set()
1439 skippedpatches = set()
1451
1440
1452 for p in mq.applied:
1441 for p in mq.applied:
1453 rev = repo[p.node].rev()
1442 rev = repo[p.node].rev()
1454 if rev in state:
1443 if rev in state:
1455 repo.ui.debug('revision %d is an mq patch (%s), finalize it.\n' %
1444 repo.ui.debug('revision %d is an mq patch (%s), finalize it.\n' %
1456 (rev, p.name))
1445 (rev, p.name))
1457 mqrebase[rev] = (p.name, isagitpatch(repo, p.name))
1446 mqrebase[rev] = (p.name, isagitpatch(repo, p.name))
1458 else:
1447 else:
1459 # Applied but not rebased, not sure this should happen
1448 # Applied but not rebased, not sure this should happen
1460 skippedpatches.add(p.name)
1449 skippedpatches.add(p.name)
1461
1450
1462 if mqrebase:
1451 if mqrebase:
1463 mq.finish(repo, mqrebase.keys())
1452 mq.finish(repo, mqrebase.keys())
1464
1453
1465 # We must start import from the newest revision
1454 # We must start import from the newest revision
1466 for rev in sorted(mqrebase, reverse=True):
1455 for rev in sorted(mqrebase, reverse=True):
1467 if rev not in skipped:
1456 if rev not in skipped:
1468 name, isgit = mqrebase[rev]
1457 name, isgit = mqrebase[rev]
1469 repo.ui.note(_('updating mq patch %s to %d:%s\n') %
1458 repo.ui.note(_('updating mq patch %s to %d:%s\n') %
1470 (name, state[rev], repo[state[rev]]))
1459 (name, state[rev], repo[state[rev]]))
1471 mq.qimport(repo, (), patchname=name, git=isgit,
1460 mq.qimport(repo, (), patchname=name, git=isgit,
1472 rev=["%d" % state[rev]])
1461 rev=["%d" % state[rev]])
1473 else:
1462 else:
1474 # Rebased and skipped
1463 # Rebased and skipped
1475 skippedpatches.add(mqrebase[rev][0])
1464 skippedpatches.add(mqrebase[rev][0])
1476
1465
1477 # Patches were either applied and rebased and imported in
1466 # Patches were either applied and rebased and imported in
1478 # order, applied and removed or unapplied. Discard the removed
1467 # order, applied and removed or unapplied. Discard the removed
1479 # ones while preserving the original series order and guards.
1468 # ones while preserving the original series order and guards.
1480 newseries = [s for s in original_series
1469 newseries = [s for s in original_series
1481 if mq.guard_re.split(s, 1)[0] not in skippedpatches]
1470 if mq.guard_re.split(s, 1)[0] not in skippedpatches]
1482 mq.fullseries[:] = newseries
1471 mq.fullseries[:] = newseries
1483 mq.seriesdirty = True
1472 mq.seriesdirty = True
1484 mq.savedirty()
1473 mq.savedirty()
1485
1474
1486 def storecollapsemsg(repo, collapsemsg):
1475 def storecollapsemsg(repo, collapsemsg):
1487 'Store the collapse message to allow recovery'
1476 'Store the collapse message to allow recovery'
1488 collapsemsg = collapsemsg or ''
1477 collapsemsg = collapsemsg or ''
1489 f = repo.vfs("last-message.txt", "w")
1478 f = repo.vfs("last-message.txt", "w")
1490 f.write("%s\n" % collapsemsg)
1479 f.write("%s\n" % collapsemsg)
1491 f.close()
1480 f.close()
1492
1481
1493 def clearcollapsemsg(repo):
1482 def clearcollapsemsg(repo):
1494 'Remove collapse message file'
1483 'Remove collapse message file'
1495 repo.vfs.unlinkpath("last-message.txt", ignoremissing=True)
1484 repo.vfs.unlinkpath("last-message.txt", ignoremissing=True)
1496
1485
1497 def restorecollapsemsg(repo, isabort):
1486 def restorecollapsemsg(repo, isabort):
1498 'Restore previously stored collapse message'
1487 'Restore previously stored collapse message'
1499 try:
1488 try:
1500 f = repo.vfs("last-message.txt")
1489 f = repo.vfs("last-message.txt")
1501 collapsemsg = f.readline().strip()
1490 collapsemsg = f.readline().strip()
1502 f.close()
1491 f.close()
1503 except IOError as err:
1492 except IOError as err:
1504 if err.errno != errno.ENOENT:
1493 if err.errno != errno.ENOENT:
1505 raise
1494 raise
1506 if isabort:
1495 if isabort:
1507 # Oh well, just abort like normal
1496 # Oh well, just abort like normal
1508 collapsemsg = ''
1497 collapsemsg = ''
1509 else:
1498 else:
1510 raise error.Abort(_('missing .hg/last-message.txt for rebase'))
1499 raise error.Abort(_('missing .hg/last-message.txt for rebase'))
1511 return collapsemsg
1500 return collapsemsg
1512
1501
1513 def clearstatus(repo):
1502 def clearstatus(repo):
1514 'Remove the status files'
1503 'Remove the status files'
1515 # Make sure the active transaction won't write the state file
1504 # Make sure the active transaction won't write the state file
1516 tr = repo.currenttransaction()
1505 tr = repo.currenttransaction()
1517 if tr:
1506 if tr:
1518 tr.removefilegenerator('rebasestate')
1507 tr.removefilegenerator('rebasestate')
1519 repo.vfs.unlinkpath("rebasestate", ignoremissing=True)
1508 repo.vfs.unlinkpath("rebasestate", ignoremissing=True)
1520
1509
1521 def needupdate(repo, state):
1510 def needupdate(repo, state):
1522 '''check whether we should `update --clean` away from a merge, or if
1511 '''check whether we should `update --clean` away from a merge, or if
1523 somehow the working dir got forcibly updated, e.g. by older hg'''
1512 somehow the working dir got forcibly updated, e.g. by older hg'''
1524 parents = [p.rev() for p in repo[None].parents()]
1513 parents = [p.rev() for p in repo[None].parents()]
1525
1514
1526 # Are we in a merge state at all?
1515 # Are we in a merge state at all?
1527 if len(parents) < 2:
1516 if len(parents) < 2:
1528 return False
1517 return False
1529
1518
1530 # We should be standing on the first as-of-yet unrebased commit.
1519 # We should be standing on the first as-of-yet unrebased commit.
1531 firstunrebased = min([old for old, new in state.iteritems()
1520 firstunrebased = min([old for old, new in state.iteritems()
1532 if new == nullrev])
1521 if new == nullrev])
1533 if firstunrebased in parents:
1522 if firstunrebased in parents:
1534 return True
1523 return True
1535
1524
1536 return False
1525 return False
1537
1526
1538 def abort(repo, originalwd, destmap, state, activebookmark=None):
1527 def abort(repo, originalwd, destmap, state, activebookmark=None):
1539 '''Restore the repository to its original state. Additional args:
1528 '''Restore the repository to its original state. Additional args:
1540
1529
1541 activebookmark: the name of the bookmark that should be active after the
1530 activebookmark: the name of the bookmark that should be active after the
1542 restore'''
1531 restore'''
1543
1532
1544 try:
1533 try:
1545 # If the first commits in the rebased set get skipped during the rebase,
1534 # If the first commits in the rebased set get skipped during the rebase,
1546 # their values within the state mapping will be the dest rev id. The
1535 # their values within the state mapping will be the dest rev id. The
1547 # dstates list must must not contain the dest rev (issue4896)
1536 # dstates list must must not contain the dest rev (issue4896)
1548 dstates = [s for r, s in state.items() if s >= 0 and s != destmap[r]]
1537 dstates = [s for r, s in state.items() if s >= 0 and s != destmap[r]]
1549 immutable = [d for d in dstates if not repo[d].mutable()]
1538 immutable = [d for d in dstates if not repo[d].mutable()]
1550 cleanup = True
1539 cleanup = True
1551 if immutable:
1540 if immutable:
1552 repo.ui.warn(_("warning: can't clean up public changesets %s\n")
1541 repo.ui.warn(_("warning: can't clean up public changesets %s\n")
1553 % ', '.join(bytes(repo[r]) for r in immutable),
1542 % ', '.join(bytes(repo[r]) for r in immutable),
1554 hint=_("see 'hg help phases' for details"))
1543 hint=_("see 'hg help phases' for details"))
1555 cleanup = False
1544 cleanup = False
1556
1545
1557 descendants = set()
1546 descendants = set()
1558 if dstates:
1547 if dstates:
1559 descendants = set(repo.changelog.descendants(dstates))
1548 descendants = set(repo.changelog.descendants(dstates))
1560 if descendants - set(dstates):
1549 if descendants - set(dstates):
1561 repo.ui.warn(_("warning: new changesets detected on destination "
1550 repo.ui.warn(_("warning: new changesets detected on destination "
1562 "branch, can't strip\n"))
1551 "branch, can't strip\n"))
1563 cleanup = False
1552 cleanup = False
1564
1553
1565 if cleanup:
1554 if cleanup:
1566 shouldupdate = False
1555 shouldupdate = False
1567 rebased = [s for r, s in state.items()
1556 rebased = [s for r, s in state.items()
1568 if s >= 0 and s != destmap[r]]
1557 if s >= 0 and s != destmap[r]]
1569 if rebased:
1558 if rebased:
1570 strippoints = [
1559 strippoints = [
1571 c.node() for c in repo.set('roots(%ld)', rebased)]
1560 c.node() for c in repo.set('roots(%ld)', rebased)]
1572
1561
1573 updateifonnodes = set(rebased)
1562 updateifonnodes = set(rebased)
1574 updateifonnodes.update(destmap.values())
1563 updateifonnodes.update(destmap.values())
1575 updateifonnodes.add(originalwd)
1564 updateifonnodes.add(originalwd)
1576 shouldupdate = repo['.'].rev() in updateifonnodes
1565 shouldupdate = repo['.'].rev() in updateifonnodes
1577
1566
1578 # Update away from the rebase if necessary
1567 # Update away from the rebase if necessary
1579 if shouldupdate or needupdate(repo, state):
1568 if shouldupdate or needupdate(repo, state):
1580 mergemod.update(repo, originalwd, False, True)
1569 mergemod.update(repo, originalwd, False, True)
1581
1570
1582 # Strip from the first rebased revision
1571 # Strip from the first rebased revision
1583 if rebased:
1572 if rebased:
1584 # no backup of rebased cset versions needed
1573 # no backup of rebased cset versions needed
1585 repair.strip(repo.ui, repo, strippoints)
1574 repair.strip(repo.ui, repo, strippoints)
1586
1575
1587 if activebookmark and activebookmark in repo._bookmarks:
1576 if activebookmark and activebookmark in repo._bookmarks:
1588 bookmarks.activate(repo, activebookmark)
1577 bookmarks.activate(repo, activebookmark)
1589
1578
1590 finally:
1579 finally:
1591 clearstatus(repo)
1580 clearstatus(repo)
1592 clearcollapsemsg(repo)
1581 clearcollapsemsg(repo)
1593 repo.ui.warn(_('rebase aborted\n'))
1582 repo.ui.warn(_('rebase aborted\n'))
1594 return 0
1583 return 0
1595
1584
1596 def sortsource(destmap):
1585 def sortsource(destmap):
1597 """yield source revisions in an order that we only rebase things once
1586 """yield source revisions in an order that we only rebase things once
1598
1587
1599 If source and destination overlaps, we should filter out revisions
1588 If source and destination overlaps, we should filter out revisions
1600 depending on other revisions which hasn't been rebased yet.
1589 depending on other revisions which hasn't been rebased yet.
1601
1590
1602 Yield a sorted list of revisions each time.
1591 Yield a sorted list of revisions each time.
1603
1592
1604 For example, when rebasing A to B, B to C. This function yields [B], then
1593 For example, when rebasing A to B, B to C. This function yields [B], then
1605 [A], indicating B needs to be rebased first.
1594 [A], indicating B needs to be rebased first.
1606
1595
1607 Raise if there is a cycle so the rebase is impossible.
1596 Raise if there is a cycle so the rebase is impossible.
1608 """
1597 """
1609 srcset = set(destmap)
1598 srcset = set(destmap)
1610 while srcset:
1599 while srcset:
1611 srclist = sorted(srcset)
1600 srclist = sorted(srcset)
1612 result = []
1601 result = []
1613 for r in srclist:
1602 for r in srclist:
1614 if destmap[r] not in srcset:
1603 if destmap[r] not in srcset:
1615 result.append(r)
1604 result.append(r)
1616 if not result:
1605 if not result:
1617 raise error.Abort(_('source and destination form a cycle'))
1606 raise error.Abort(_('source and destination form a cycle'))
1618 srcset -= set(result)
1607 srcset -= set(result)
1619 yield result
1608 yield result
1620
1609
1621 def buildstate(repo, destmap, collapse):
1610 def buildstate(repo, destmap, collapse):
1622 '''Define which revisions are going to be rebased and where
1611 '''Define which revisions are going to be rebased and where
1623
1612
1624 repo: repo
1613 repo: repo
1625 destmap: {srcrev: destrev}
1614 destmap: {srcrev: destrev}
1626 '''
1615 '''
1627 rebaseset = destmap.keys()
1616 rebaseset = destmap.keys()
1628 originalwd = repo['.'].rev()
1617 originalwd = repo['.'].rev()
1629
1618
1630 # This check isn't strictly necessary, since mq detects commits over an
1619 # This check isn't strictly necessary, since mq detects commits over an
1631 # applied patch. But it prevents messing up the working directory when
1620 # applied patch. But it prevents messing up the working directory when
1632 # a partially completed rebase is blocked by mq.
1621 # a partially completed rebase is blocked by mq.
1633 if 'qtip' in repo.tags():
1622 if 'qtip' in repo.tags():
1634 mqapplied = set(repo[s.node].rev() for s in repo.mq.applied)
1623 mqapplied = set(repo[s.node].rev() for s in repo.mq.applied)
1635 if set(destmap.values()) & mqapplied:
1624 if set(destmap.values()) & mqapplied:
1636 raise error.Abort(_('cannot rebase onto an applied mq patch'))
1625 raise error.Abort(_('cannot rebase onto an applied mq patch'))
1637
1626
1638 # Get "cycle" error early by exhausting the generator.
1627 # Get "cycle" error early by exhausting the generator.
1639 sortedsrc = list(sortsource(destmap)) # a list of sorted revs
1628 sortedsrc = list(sortsource(destmap)) # a list of sorted revs
1640 if not sortedsrc:
1629 if not sortedsrc:
1641 raise error.Abort(_('no matching revisions'))
1630 raise error.Abort(_('no matching revisions'))
1642
1631
1643 # Only check the first batch of revisions to rebase not depending on other
1632 # Only check the first batch of revisions to rebase not depending on other
1644 # rebaseset. This means "source is ancestor of destination" for the second
1633 # rebaseset. This means "source is ancestor of destination" for the second
1645 # (and following) batches of revisions are not checked here. We rely on
1634 # (and following) batches of revisions are not checked here. We rely on
1646 # "defineparents" to do that check.
1635 # "defineparents" to do that check.
1647 roots = list(repo.set('roots(%ld)', sortedsrc[0]))
1636 roots = list(repo.set('roots(%ld)', sortedsrc[0]))
1648 if not roots:
1637 if not roots:
1649 raise error.Abort(_('no matching revisions'))
1638 raise error.Abort(_('no matching revisions'))
1650 def revof(r):
1639 def revof(r):
1651 return r.rev()
1640 return r.rev()
1652 roots = sorted(roots, key=revof)
1641 roots = sorted(roots, key=revof)
1653 state = dict.fromkeys(rebaseset, revtodo)
1642 state = dict.fromkeys(rebaseset, revtodo)
1654 emptyrebase = (len(sortedsrc) == 1)
1643 emptyrebase = (len(sortedsrc) == 1)
1655 for root in roots:
1644 for root in roots:
1656 dest = repo[destmap[root.rev()]]
1645 dest = repo[destmap[root.rev()]]
1657 commonbase = root.ancestor(dest)
1646 commonbase = root.ancestor(dest)
1658 if commonbase == root:
1647 if commonbase == root:
1659 raise error.Abort(_('source is ancestor of destination'))
1648 raise error.Abort(_('source is ancestor of destination'))
1660 if commonbase == dest:
1649 if commonbase == dest:
1661 wctx = repo[None]
1650 wctx = repo[None]
1662 if dest == wctx.p1():
1651 if dest == wctx.p1():
1663 # when rebasing to '.', it will use the current wd branch name
1652 # when rebasing to '.', it will use the current wd branch name
1664 samebranch = root.branch() == wctx.branch()
1653 samebranch = root.branch() == wctx.branch()
1665 else:
1654 else:
1666 samebranch = root.branch() == dest.branch()
1655 samebranch = root.branch() == dest.branch()
1667 if not collapse and samebranch and dest in root.parents():
1656 if not collapse and samebranch and dest in root.parents():
1668 # mark the revision as done by setting its new revision
1657 # mark the revision as done by setting its new revision
1669 # equal to its old (current) revisions
1658 # equal to its old (current) revisions
1670 state[root.rev()] = root.rev()
1659 state[root.rev()] = root.rev()
1671 repo.ui.debug('source is a child of destination\n')
1660 repo.ui.debug('source is a child of destination\n')
1672 continue
1661 continue
1673
1662
1674 emptyrebase = False
1663 emptyrebase = False
1675 repo.ui.debug('rebase onto %s starting from %s\n' % (dest, root))
1664 repo.ui.debug('rebase onto %s starting from %s\n' % (dest, root))
1676 if emptyrebase:
1665 if emptyrebase:
1677 return None
1666 return None
1678 for rev in sorted(state):
1667 for rev in sorted(state):
1679 parents = [p for p in repo.changelog.parentrevs(rev) if p != nullrev]
1668 parents = [p for p in repo.changelog.parentrevs(rev) if p != nullrev]
1680 # if all parents of this revision are done, then so is this revision
1669 # if all parents of this revision are done, then so is this revision
1681 if parents and all((state.get(p) == p for p in parents)):
1670 if parents and all((state.get(p) == p for p in parents)):
1682 state[rev] = rev
1671 state[rev] = rev
1683 return originalwd, destmap, state
1672 return originalwd, destmap, state
1684
1673
1685 def clearrebased(ui, repo, destmap, state, skipped, collapsedas=None,
1674 def clearrebased(ui, repo, destmap, state, skipped, collapsedas=None,
1686 keepf=False, fm=None):
1675 keepf=False, fm=None):
1687 """dispose of rebased revision at the end of the rebase
1676 """dispose of rebased revision at the end of the rebase
1688
1677
1689 If `collapsedas` is not None, the rebase was a collapse whose result if the
1678 If `collapsedas` is not None, the rebase was a collapse whose result if the
1690 `collapsedas` node.
1679 `collapsedas` node.
1691
1680
1692 If `keepf` is not True, the rebase has --keep set and no nodes should be
1681 If `keepf` is not True, the rebase has --keep set and no nodes should be
1693 removed (but bookmarks still need to be moved).
1682 removed (but bookmarks still need to be moved).
1694 """
1683 """
1695 tonode = repo.changelog.node
1684 tonode = repo.changelog.node
1696 replacements = {}
1685 replacements = {}
1697 moves = {}
1686 moves = {}
1698 for rev, newrev in sorted(state.items()):
1687 for rev, newrev in sorted(state.items()):
1699 if newrev >= 0 and newrev != rev:
1688 if newrev >= 0 and newrev != rev:
1700 oldnode = tonode(rev)
1689 oldnode = tonode(rev)
1701 newnode = collapsedas or tonode(newrev)
1690 newnode = collapsedas or tonode(newrev)
1702 moves[oldnode] = newnode
1691 moves[oldnode] = newnode
1703 if not keepf:
1692 if not keepf:
1704 if rev in skipped:
1693 if rev in skipped:
1705 succs = ()
1694 succs = ()
1706 else:
1695 else:
1707 succs = (newnode,)
1696 succs = (newnode,)
1708 replacements[oldnode] = succs
1697 replacements[oldnode] = succs
1709 scmutil.cleanupnodes(repo, replacements, 'rebase', moves)
1698 scmutil.cleanupnodes(repo, replacements, 'rebase', moves)
1710 if fm:
1699 if fm:
1711 hf = fm.hexfunc
1700 hf = fm.hexfunc
1712 fl = fm.formatlist
1701 fl = fm.formatlist
1713 fd = fm.formatdict
1702 fd = fm.formatdict
1714 nodechanges = fd({hf(oldn): fl([hf(n) for n in newn], name='node')
1703 nodechanges = fd({hf(oldn): fl([hf(n) for n in newn], name='node')
1715 for oldn, newn in replacements.iteritems()},
1704 for oldn, newn in replacements.iteritems()},
1716 key="oldnode", value="newnodes")
1705 key="oldnode", value="newnodes")
1717 fm.data(nodechanges=nodechanges)
1706 fm.data(nodechanges=nodechanges)
1718
1707
1719 def pullrebase(orig, ui, repo, *args, **opts):
1708 def pullrebase(orig, ui, repo, *args, **opts):
1720 'Call rebase after pull if the latter has been invoked with --rebase'
1709 'Call rebase after pull if the latter has been invoked with --rebase'
1721 ret = None
1710 ret = None
1722 if opts.get(r'rebase'):
1711 if opts.get(r'rebase'):
1723 if ui.configbool('commands', 'rebase.requiredest'):
1712 if ui.configbool('commands', 'rebase.requiredest'):
1724 msg = _('rebase destination required by configuration')
1713 msg = _('rebase destination required by configuration')
1725 hint = _('use hg pull followed by hg rebase -d DEST')
1714 hint = _('use hg pull followed by hg rebase -d DEST')
1726 raise error.Abort(msg, hint=hint)
1715 raise error.Abort(msg, hint=hint)
1727
1716
1728 with repo.wlock(), repo.lock():
1717 with repo.wlock(), repo.lock():
1729 if opts.get(r'update'):
1718 if opts.get(r'update'):
1730 del opts[r'update']
1719 del opts[r'update']
1731 ui.debug('--update and --rebase are not compatible, ignoring '
1720 ui.debug('--update and --rebase are not compatible, ignoring '
1732 'the update flag\n')
1721 'the update flag\n')
1733
1722
1734 cmdutil.checkunfinished(repo)
1723 cmdutil.checkunfinished(repo)
1735 cmdutil.bailifchanged(repo, hint=_('cannot pull with rebase: '
1724 cmdutil.bailifchanged(repo, hint=_('cannot pull with rebase: '
1736 'please commit or shelve your changes first'))
1725 'please commit or shelve your changes first'))
1737
1726
1738 revsprepull = len(repo)
1727 revsprepull = len(repo)
1739 origpostincoming = commands.postincoming
1728 origpostincoming = commands.postincoming
1740 def _dummy(*args, **kwargs):
1729 def _dummy(*args, **kwargs):
1741 pass
1730 pass
1742 commands.postincoming = _dummy
1731 commands.postincoming = _dummy
1743 try:
1732 try:
1744 ret = orig(ui, repo, *args, **opts)
1733 ret = orig(ui, repo, *args, **opts)
1745 finally:
1734 finally:
1746 commands.postincoming = origpostincoming
1735 commands.postincoming = origpostincoming
1747 revspostpull = len(repo)
1736 revspostpull = len(repo)
1748 if revspostpull > revsprepull:
1737 if revspostpull > revsprepull:
1749 # --rev option from pull conflict with rebase own --rev
1738 # --rev option from pull conflict with rebase own --rev
1750 # dropping it
1739 # dropping it
1751 if r'rev' in opts:
1740 if r'rev' in opts:
1752 del opts[r'rev']
1741 del opts[r'rev']
1753 # positional argument from pull conflicts with rebase's own
1742 # positional argument from pull conflicts with rebase's own
1754 # --source.
1743 # --source.
1755 if r'source' in opts:
1744 if r'source' in opts:
1756 del opts[r'source']
1745 del opts[r'source']
1757 # revsprepull is the len of the repo, not revnum of tip.
1746 # revsprepull is the len of the repo, not revnum of tip.
1758 destspace = list(repo.changelog.revs(start=revsprepull))
1747 destspace = list(repo.changelog.revs(start=revsprepull))
1759 opts[r'_destspace'] = destspace
1748 opts[r'_destspace'] = destspace
1760 try:
1749 try:
1761 rebase(ui, repo, **opts)
1750 rebase(ui, repo, **opts)
1762 except error.NoMergeDestAbort:
1751 except error.NoMergeDestAbort:
1763 # we can maybe update instead
1752 # we can maybe update instead
1764 rev, _a, _b = destutil.destupdate(repo)
1753 rev, _a, _b = destutil.destupdate(repo)
1765 if rev == repo['.'].rev():
1754 if rev == repo['.'].rev():
1766 ui.status(_('nothing to rebase\n'))
1755 ui.status(_('nothing to rebase\n'))
1767 else:
1756 else:
1768 ui.status(_('nothing to rebase - updating instead\n'))
1757 ui.status(_('nothing to rebase - updating instead\n'))
1769 # not passing argument to get the bare update behavior
1758 # not passing argument to get the bare update behavior
1770 # with warning and trumpets
1759 # with warning and trumpets
1771 commands.update(ui, repo)
1760 commands.update(ui, repo)
1772 else:
1761 else:
1773 if opts.get(r'tool'):
1762 if opts.get(r'tool'):
1774 raise error.Abort(_('--tool can only be used with --rebase'))
1763 raise error.Abort(_('--tool can only be used with --rebase'))
1775 ret = orig(ui, repo, *args, **opts)
1764 ret = orig(ui, repo, *args, **opts)
1776
1765
1777 return ret
1766 return ret
1778
1767
1779 def _filterobsoleterevs(repo, revs):
1768 def _filterobsoleterevs(repo, revs):
1780 """returns a set of the obsolete revisions in revs"""
1769 """returns a set of the obsolete revisions in revs"""
1781 return set(r for r in revs if repo[r].obsolete())
1770 return set(r for r in revs if repo[r].obsolete())
1782
1771
1783 def _computeobsoletenotrebased(repo, rebaseobsrevs, destmap):
1772 def _computeobsoletenotrebased(repo, rebaseobsrevs, destmap):
1784 """Return (obsoletenotrebased, obsoletewithoutsuccessorindestination).
1773 """Return (obsoletenotrebased, obsoletewithoutsuccessorindestination).
1785
1774
1786 `obsoletenotrebased` is a mapping mapping obsolete => successor for all
1775 `obsoletenotrebased` is a mapping mapping obsolete => successor for all
1787 obsolete nodes to be rebased given in `rebaseobsrevs`.
1776 obsolete nodes to be rebased given in `rebaseobsrevs`.
1788
1777
1789 `obsoletewithoutsuccessorindestination` is a set with obsolete revisions
1778 `obsoletewithoutsuccessorindestination` is a set with obsolete revisions
1790 without a successor in destination.
1779 without a successor in destination.
1791
1780
1792 `obsoleteextinctsuccessors` is a set of obsolete revisions with only
1781 `obsoleteextinctsuccessors` is a set of obsolete revisions with only
1793 obsolete successors.
1782 obsolete successors.
1794 """
1783 """
1795 obsoletenotrebased = {}
1784 obsoletenotrebased = {}
1796 obsoletewithoutsuccessorindestination = set([])
1785 obsoletewithoutsuccessorindestination = set([])
1797 obsoleteextinctsuccessors = set([])
1786 obsoleteextinctsuccessors = set([])
1798
1787
1799 assert repo.filtername is None
1788 assert repo.filtername is None
1800 cl = repo.changelog
1789 cl = repo.changelog
1801 nodemap = cl.nodemap
1790 nodemap = cl.nodemap
1802 extinctnodes = set(cl.node(r) for r in repo.revs('extinct()'))
1791 extinctnodes = set(cl.node(r) for r in repo.revs('extinct()'))
1803 for srcrev in rebaseobsrevs:
1792 for srcrev in rebaseobsrevs:
1804 srcnode = cl.node(srcrev)
1793 srcnode = cl.node(srcrev)
1805 destnode = cl.node(destmap[srcrev])
1794 destnode = cl.node(destmap[srcrev])
1806 # XXX: more advanced APIs are required to handle split correctly
1795 # XXX: more advanced APIs are required to handle split correctly
1807 successors = set(obsutil.allsuccessors(repo.obsstore, [srcnode]))
1796 successors = set(obsutil.allsuccessors(repo.obsstore, [srcnode]))
1808 # obsutil.allsuccessors includes node itself
1797 # obsutil.allsuccessors includes node itself
1809 successors.remove(srcnode)
1798 successors.remove(srcnode)
1810 if successors.issubset(extinctnodes):
1799 if successors.issubset(extinctnodes):
1811 # all successors are extinct
1800 # all successors are extinct
1812 obsoleteextinctsuccessors.add(srcrev)
1801 obsoleteextinctsuccessors.add(srcrev)
1813 if not successors:
1802 if not successors:
1814 # no successor
1803 # no successor
1815 obsoletenotrebased[srcrev] = None
1804 obsoletenotrebased[srcrev] = None
1816 else:
1805 else:
1817 for succnode in successors:
1806 for succnode in successors:
1818 if succnode not in nodemap:
1807 if succnode not in nodemap:
1819 continue
1808 continue
1820 if cl.isancestor(succnode, destnode):
1809 if cl.isancestor(succnode, destnode):
1821 obsoletenotrebased[srcrev] = nodemap[succnode]
1810 obsoletenotrebased[srcrev] = nodemap[succnode]
1822 break
1811 break
1823 else:
1812 else:
1824 # If 'srcrev' has a successor in rebase set but none in
1813 # If 'srcrev' has a successor in rebase set but none in
1825 # destination (which would be catched above), we shall skip it
1814 # destination (which would be catched above), we shall skip it
1826 # and its descendants to avoid divergence.
1815 # and its descendants to avoid divergence.
1827 if any(nodemap[s] in destmap for s in successors):
1816 if any(nodemap[s] in destmap for s in successors):
1828 obsoletewithoutsuccessorindestination.add(srcrev)
1817 obsoletewithoutsuccessorindestination.add(srcrev)
1829
1818
1830 return (
1819 return (
1831 obsoletenotrebased,
1820 obsoletenotrebased,
1832 obsoletewithoutsuccessorindestination,
1821 obsoletewithoutsuccessorindestination,
1833 obsoleteextinctsuccessors,
1822 obsoleteextinctsuccessors,
1834 )
1823 )
1835
1824
1836 def summaryhook(ui, repo):
1825 def summaryhook(ui, repo):
1837 if not repo.vfs.exists('rebasestate'):
1826 if not repo.vfs.exists('rebasestate'):
1838 return
1827 return
1839 try:
1828 try:
1840 rbsrt = rebaseruntime(repo, ui, {})
1829 rbsrt = rebaseruntime(repo, ui, {})
1841 rbsrt.restorestatus()
1830 rbsrt.restorestatus()
1842 state = rbsrt.state
1831 state = rbsrt.state
1843 except error.RepoLookupError:
1832 except error.RepoLookupError:
1844 # i18n: column positioning for "hg summary"
1833 # i18n: column positioning for "hg summary"
1845 msg = _('rebase: (use "hg rebase --abort" to clear broken state)\n')
1834 msg = _('rebase: (use "hg rebase --abort" to clear broken state)\n')
1846 ui.write(msg)
1835 ui.write(msg)
1847 return
1836 return
1848 numrebased = len([i for i in state.itervalues() if i >= 0])
1837 numrebased = len([i for i in state.itervalues() if i >= 0])
1849 # i18n: column positioning for "hg summary"
1838 # i18n: column positioning for "hg summary"
1850 ui.write(_('rebase: %s, %s (rebase --continue)\n') %
1839 ui.write(_('rebase: %s, %s (rebase --continue)\n') %
1851 (ui.label(_('%d rebased'), 'rebase.rebased') % numrebased,
1840 (ui.label(_('%d rebased'), 'rebase.rebased') % numrebased,
1852 ui.label(_('%d remaining'), 'rebase.remaining') %
1841 ui.label(_('%d remaining'), 'rebase.remaining') %
1853 (len(state) - numrebased)))
1842 (len(state) - numrebased)))
1854
1843
1855 def uisetup(ui):
1844 def uisetup(ui):
1856 #Replace pull with a decorator to provide --rebase option
1845 #Replace pull with a decorator to provide --rebase option
1857 entry = extensions.wrapcommand(commands.table, 'pull', pullrebase)
1846 entry = extensions.wrapcommand(commands.table, 'pull', pullrebase)
1858 entry[1].append(('', 'rebase', None,
1847 entry[1].append(('', 'rebase', None,
1859 _("rebase working directory to branch head")))
1848 _("rebase working directory to branch head")))
1860 entry[1].append(('t', 'tool', '',
1849 entry[1].append(('t', 'tool', '',
1861 _("specify merge tool for rebase")))
1850 _("specify merge tool for rebase")))
1862 cmdutil.summaryhooks.add('rebase', summaryhook)
1851 cmdutil.summaryhooks.add('rebase', summaryhook)
1863 cmdutil.unfinishedstates.append(
1852 cmdutil.unfinishedstates.append(
1864 ['rebasestate', False, False, _('rebase in progress'),
1853 ['rebasestate', False, False, _('rebase in progress'),
1865 _("use 'hg rebase --continue' or 'hg rebase --abort'")])
1854 _("use 'hg rebase --continue' or 'hg rebase --abort'")])
1866 cmdutil.afterresolvedstates.append(
1855 cmdutil.afterresolvedstates.append(
1867 ['rebasestate', _('hg rebase --continue')])
1856 ['rebasestate', _('hg rebase --continue')])
@@ -1,159 +1,158 b''
1 #require symlink execbit
1 #require symlink execbit
2 $ cat << EOF >> $HGRCPATH
2 $ cat << EOF >> $HGRCPATH
3 > [extensions]
3 > [extensions]
4 > amend=
4 > amend=
5 > rebase=
5 > rebase=
6 > debugdrawdag=$TESTDIR/drawdag.py
6 > debugdrawdag=$TESTDIR/drawdag.py
7 > [rebase]
7 > [rebase]
8 > experimental.inmemory=1
8 > experimental.inmemory=1
9 > [diff]
9 > [diff]
10 > git=1
10 > git=1
11 > [alias]
11 > [alias]
12 > tglog = log -G --template "{rev}: {node|short} '{desc}'\n"
12 > tglog = log -G --template "{rev}: {node|short} '{desc}'\n"
13 > EOF
13 > EOF
14
14
15 Rebase a simple DAG:
15 Rebase a simple DAG:
16 $ hg init repo1
16 $ hg init repo1
17 $ cd repo1
17 $ cd repo1
18 $ hg debugdrawdag <<'EOS'
18 $ hg debugdrawdag <<'EOS'
19 > c b
19 > c b
20 > |/
20 > |/
21 > d
21 > d
22 > |
22 > |
23 > a
23 > a
24 > EOS
24 > EOS
25 $ hg up -C a
25 $ hg up -C a
26 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
26 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
27 $ hg tglog
27 $ hg tglog
28 o 3: 814f6bd05178 'c'
28 o 3: 814f6bd05178 'c'
29 |
29 |
30 | o 2: db0e82a16a62 'b'
30 | o 2: db0e82a16a62 'b'
31 |/
31 |/
32 o 1: 02952614a83d 'd'
32 o 1: 02952614a83d 'd'
33 |
33 |
34 @ 0: b173517d0057 'a'
34 @ 0: b173517d0057 'a'
35
35
36 $ hg cat -r 3 c
36 $ hg cat -r 3 c
37 c (no-eol)
37 c (no-eol)
38 $ hg cat -r 2 b
38 $ hg cat -r 2 b
39 b (no-eol)
39 b (no-eol)
40 $ hg rebase --debug -r b -d c | grep rebasing
40 $ hg rebase --debug -r b -d c | grep rebasing
41 rebasing in-memory
41 rebasing in-memory
42 rebasing 2:db0e82a16a62 "b" (b)
42 rebasing 2:db0e82a16a62 "b" (b)
43 $ hg tglog
43 $ hg tglog
44 o 3: ca58782ad1e4 'b'
44 o 3: ca58782ad1e4 'b'
45 |
45 |
46 o 2: 814f6bd05178 'c'
46 o 2: 814f6bd05178 'c'
47 |
47 |
48 o 1: 02952614a83d 'd'
48 o 1: 02952614a83d 'd'
49 |
49 |
50 @ 0: b173517d0057 'a'
50 @ 0: b173517d0057 'a'
51
51
52 $ hg cat -r 3 b
52 $ hg cat -r 3 b
53 b (no-eol)
53 b (no-eol)
54 $ hg cat -r 2 c
54 $ hg cat -r 2 c
55 c (no-eol)
55 c (no-eol)
56
56
57 Case 2:
57 Case 2:
58 $ hg init repo2
58 $ hg init repo2
59 $ cd repo2
59 $ cd repo2
60 $ hg debugdrawdag <<'EOS'
60 $ hg debugdrawdag <<'EOS'
61 > c b
61 > c b
62 > |/
62 > |/
63 > d
63 > d
64 > |
64 > |
65 > a
65 > a
66 > EOS
66 > EOS
67
67
68 Add a symlink and executable file:
68 Add a symlink and executable file:
69 $ hg up -C c
69 $ hg up -C c
70 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
70 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
71 $ ln -s somefile e
71 $ ln -s somefile e
72 $ echo f > f
72 $ echo f > f
73 $ chmod +x f
73 $ chmod +x f
74 $ hg add e f
74 $ hg add e f
75 $ hg amend -q
75 $ hg amend -q
76 $ hg up -Cq a
76 $ hg up -Cq a
77
77
78 Write files to the working copy, and ensure they're still there after the rebase
78 Write files to the working copy, and ensure they're still there after the rebase
79 $ echo "abc" > a
79 $ echo "abc" > a
80 $ ln -s def b
80 $ ln -s def b
81 $ echo "ghi" > c
81 $ echo "ghi" > c
82 $ echo "jkl" > d
82 $ echo "jkl" > d
83 $ echo "mno" > e
83 $ echo "mno" > e
84 $ hg tglog
84 $ hg tglog
85 o 3: f56b71190a8f 'c'
85 o 3: f56b71190a8f 'c'
86 |
86 |
87 | o 2: db0e82a16a62 'b'
87 | o 2: db0e82a16a62 'b'
88 |/
88 |/
89 o 1: 02952614a83d 'd'
89 o 1: 02952614a83d 'd'
90 |
90 |
91 @ 0: b173517d0057 'a'
91 @ 0: b173517d0057 'a'
92
92
93 $ hg cat -r 3 c
93 $ hg cat -r 3 c
94 c (no-eol)
94 c (no-eol)
95 $ hg cat -r 2 b
95 $ hg cat -r 2 b
96 b (no-eol)
96 b (no-eol)
97 $ hg cat -r 3 e
97 $ hg cat -r 3 e
98 somefile (no-eol)
98 somefile (no-eol)
99 $ hg rebase --debug -s b -d a | grep rebasing
99 $ hg rebase --debug -s b -d a | grep rebasing
100 rebasing in-memory
100 rebasing in-memory
101 rebasing 2:db0e82a16a62 "b" (b)
101 rebasing 2:db0e82a16a62 "b" (b)
102 $ hg tglog
102 $ hg tglog
103 o 3: fc055c3b4d33 'b'
103 o 3: fc055c3b4d33 'b'
104 |
104 |
105 | o 2: f56b71190a8f 'c'
105 | o 2: f56b71190a8f 'c'
106 | |
106 | |
107 | o 1: 02952614a83d 'd'
107 | o 1: 02952614a83d 'd'
108 |/
108 |/
109 @ 0: b173517d0057 'a'
109 @ 0: b173517d0057 'a'
110
110
111 $ hg cat -r 2 c
111 $ hg cat -r 2 c
112 c (no-eol)
112 c (no-eol)
113 $ hg cat -r 3 b
113 $ hg cat -r 3 b
114 b (no-eol)
114 b (no-eol)
115 $ hg rebase --debug -s 1 -d 3 | grep rebasing
115 $ hg rebase --debug -s 1 -d 3 | grep rebasing
116 rebasing in-memory
116 rebasing in-memory
117 rebasing 1:02952614a83d "d" (d)
117 rebasing 1:02952614a83d "d" (d)
118 rebasing 2:f56b71190a8f "c"
118 rebasing 2:f56b71190a8f "c"
119 $ hg tglog
119 $ hg tglog
120 o 3: 753feb6fd12a 'c'
120 o 3: 753feb6fd12a 'c'
121 |
121 |
122 o 2: 09c044d2cb43 'd'
122 o 2: 09c044d2cb43 'd'
123 |
123 |
124 o 1: fc055c3b4d33 'b'
124 o 1: fc055c3b4d33 'b'
125 |
125 |
126 @ 0: b173517d0057 'a'
126 @ 0: b173517d0057 'a'
127
127
128 Ensure working copy files are still there:
128 Ensure working copy files are still there:
129 $ cat a
129 $ cat a
130 abc
130 abc
131 $ readlink.py b
131 $ readlink.py b
132 b -> def
132 b -> def
133 $ cat e
133 $ cat e
134 mno
134 mno
135
135
136 Ensure symlink and executable files were rebased properly:
136 Ensure symlink and executable files were rebased properly:
137 $ hg up -Cq 3
137 $ hg up -Cq 3
138 $ readlink.py e
138 $ readlink.py e
139 e -> somefile
139 e -> somefile
140 $ ls -l f | cut -c -10
140 $ ls -l f | cut -c -10
141 -rwxr-xr-x
141 -rwxr-xr-x
142
142
143 Rebase the working copy parent, which should default to an on-disk merge even if
143 Rebase the working copy parent
144 we requested in-memory.
145 $ hg up -C 3
144 $ hg up -C 3
146 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
145 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
147 $ hg rebase -r 3 -d 0 --debug | grep rebasing
146 $ hg rebase -r 3 -d 0 --debug | grep rebasing
148 rebasing on disk
147 rebasing in-memory
149 rebasing 3:753feb6fd12a "c" (tip)
148 rebasing 3:753feb6fd12a "c" (tip)
150 $ hg tglog
149 $ hg tglog
151 @ 3: 844a7de3e617 'c'
150 @ 3: 844a7de3e617 'c'
152 |
151 |
153 | o 2: 09c044d2cb43 'd'
152 | o 2: 09c044d2cb43 'd'
154 | |
153 | |
155 | o 1: fc055c3b4d33 'b'
154 | o 1: fc055c3b4d33 'b'
156 |/
155 |/
157 o 0: b173517d0057 'a'
156 o 0: b173517d0057 'a'
158
157
159
158
General Comments 0
You need to be logged in to leave comments. Login now