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