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