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