##// END OF EJS Templates
rebase: move local variable 'currentbookmarks' to the RR class
Kostia Balytskyi -
r29475:fb7fc877 default
parent child Browse files
Show More
@@ -1,1441 +1,1443 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,
24 hex,
25 nullid,
25 nullid,
26 nullrev,
26 nullrev,
27 short,
27 short,
28 )
28 )
29 from mercurial import (
29 from mercurial import (
30 bookmarks,
30 bookmarks,
31 cmdutil,
31 cmdutil,
32 commands,
32 commands,
33 copies,
33 copies,
34 destutil,
34 destutil,
35 error,
35 error,
36 extensions,
36 extensions,
37 hg,
37 hg,
38 lock,
38 lock,
39 merge,
39 merge,
40 obsolete,
40 obsolete,
41 patch,
41 patch,
42 phases,
42 phases,
43 registrar,
43 registrar,
44 repair,
44 repair,
45 repoview,
45 repoview,
46 revset,
46 revset,
47 scmutil,
47 scmutil,
48 util,
48 util,
49 )
49 )
50
50
51 release = lock.release
51 release = lock.release
52 templateopts = commands.templateopts
52 templateopts = commands.templateopts
53
53
54 # The following constants are used throughout the rebase module. The ordering of
54 # The following constants are used throughout the rebase module. The ordering of
55 # their values must be maintained.
55 # their values must be maintained.
56
56
57 # Indicates that a revision needs to be rebased
57 # Indicates that a revision needs to be rebased
58 revtodo = -1
58 revtodo = -1
59 nullmerge = -2
59 nullmerge = -2
60 revignored = -3
60 revignored = -3
61 # successor in rebase destination
61 # successor in rebase destination
62 revprecursor = -4
62 revprecursor = -4
63 # plain prune (no successor)
63 # plain prune (no successor)
64 revpruned = -5
64 revpruned = -5
65 revskipped = (revignored, revprecursor, revpruned)
65 revskipped = (revignored, revprecursor, revpruned)
66
66
67 cmdtable = {}
67 cmdtable = {}
68 command = cmdutil.command(cmdtable)
68 command = cmdutil.command(cmdtable)
69 # Note for extension authors: ONLY specify testedwith = 'internal' for
69 # Note for extension authors: ONLY specify testedwith = 'internal' for
70 # extensions which SHIP WITH MERCURIAL. Non-mainline extensions should
70 # extensions which SHIP WITH MERCURIAL. Non-mainline extensions should
71 # be specifying the version(s) of Mercurial they are tested with, or
71 # be specifying the version(s) of Mercurial they are tested with, or
72 # leave the attribute unspecified.
72 # leave the attribute unspecified.
73 testedwith = 'internal'
73 testedwith = 'internal'
74
74
75 def _nothingtorebase():
75 def _nothingtorebase():
76 return 1
76 return 1
77
77
78 def _savegraft(ctx, extra):
78 def _savegraft(ctx, extra):
79 s = ctx.extra().get('source', None)
79 s = ctx.extra().get('source', None)
80 if s is not None:
80 if s is not None:
81 extra['source'] = s
81 extra['source'] = s
82 s = ctx.extra().get('intermediate-source', None)
82 s = ctx.extra().get('intermediate-source', None)
83 if s is not None:
83 if s is not None:
84 extra['intermediate-source'] = s
84 extra['intermediate-source'] = s
85
85
86 def _savebranch(ctx, extra):
86 def _savebranch(ctx, extra):
87 extra['branch'] = ctx.branch()
87 extra['branch'] = ctx.branch()
88
88
89 def _makeextrafn(copiers):
89 def _makeextrafn(copiers):
90 """make an extrafn out of the given copy-functions.
90 """make an extrafn out of the given copy-functions.
91
91
92 A copy function takes a context and an extra dict, and mutates the
92 A copy function takes a context and an extra dict, and mutates the
93 extra dict as needed based on the given context.
93 extra dict as needed based on the given context.
94 """
94 """
95 def extrafn(ctx, extra):
95 def extrafn(ctx, extra):
96 for c in copiers:
96 for c in copiers:
97 c(ctx, extra)
97 c(ctx, extra)
98 return extrafn
98 return extrafn
99
99
100 def _destrebase(repo, sourceset, destspace=None):
100 def _destrebase(repo, sourceset, destspace=None):
101 """small wrapper around destmerge to pass the right extra args
101 """small wrapper around destmerge to pass the right extra args
102
102
103 Please wrap destutil.destmerge instead."""
103 Please wrap destutil.destmerge instead."""
104 return destutil.destmerge(repo, action='rebase', sourceset=sourceset,
104 return destutil.destmerge(repo, action='rebase', sourceset=sourceset,
105 onheadcheck=False, destspace=destspace)
105 onheadcheck=False, destspace=destspace)
106
106
107 revsetpredicate = registrar.revsetpredicate()
107 revsetpredicate = registrar.revsetpredicate()
108
108
109 @revsetpredicate('_destrebase')
109 @revsetpredicate('_destrebase')
110 def _revsetdestrebase(repo, subset, x):
110 def _revsetdestrebase(repo, subset, x):
111 # ``_rebasedefaultdest()``
111 # ``_rebasedefaultdest()``
112
112
113 # default destination for rebase.
113 # default destination for rebase.
114 # # XXX: Currently private because I expect the signature to change.
114 # # XXX: Currently private because I expect the signature to change.
115 # # XXX: - bailing out in case of ambiguity vs returning all data.
115 # # XXX: - bailing out in case of ambiguity vs returning all data.
116 # i18n: "_rebasedefaultdest" is a keyword
116 # i18n: "_rebasedefaultdest" is a keyword
117 sourceset = None
117 sourceset = None
118 if x is not None:
118 if x is not None:
119 sourceset = revset.getset(repo, revset.fullreposet(repo), x)
119 sourceset = revset.getset(repo, revset.fullreposet(repo), x)
120 return subset & revset.baseset([_destrebase(repo, sourceset)])
120 return subset & revset.baseset([_destrebase(repo, sourceset)])
121
121
122 class rebaseruntime(object):
122 class rebaseruntime(object):
123 """This class is a container for rebase runtime state"""
123 """This class is a container for rebase runtime state"""
124 def __init__(self, repo, ui, opts=None):
124 def __init__(self, repo, ui, opts=None):
125 if opts is None:
125 if opts is None:
126 opts = {}
126 opts = {}
127
127
128 self.repo = repo
128 self.repo = repo
129 self.ui = ui
129 self.ui = ui
130 self.opts = opts
130 self.opts = opts
131 self.originalwd = None
131 self.originalwd = None
132 self.external = nullrev
132 self.external = nullrev
133 # Mapping between the old revision id and either what is the new rebased
133 # Mapping between the old revision id and either what is the new rebased
134 # revision or what needs to be done with the old revision. The state
134 # revision or what needs to be done with the old revision. The state
135 # dict will be what contains most of the rebase progress state.
135 # dict will be what contains most of the rebase progress state.
136 self.state = {}
136 self.state = {}
137 self.activebookmark = None
137 self.activebookmark = None
138 self.currentbookmarks = None
138 self.target = None
139 self.target = None
139 self.skipped = set()
140 self.skipped = set()
140 self.targetancestors = set()
141 self.targetancestors = set()
141
142
142 self.collapsef = opts.get('collapse', False)
143 self.collapsef = opts.get('collapse', False)
143 self.collapsemsg = cmdutil.logmessage(ui, opts)
144 self.collapsemsg = cmdutil.logmessage(ui, opts)
144 self.date = opts.get('date', None)
145 self.date = opts.get('date', None)
145
146
146 e = opts.get('extrafn') # internal, used by e.g. hgsubversion
147 e = opts.get('extrafn') # internal, used by e.g. hgsubversion
147 self.extrafns = [_savegraft]
148 self.extrafns = [_savegraft]
148 if e:
149 if e:
149 self.extrafns = [e]
150 self.extrafns = [e]
150
151
151 self.keepf = opts.get('keep', False)
152 self.keepf = opts.get('keep', False)
152 self.keepbranchesf = opts.get('keepbranches', False)
153 self.keepbranchesf = opts.get('keepbranches', False)
153 # keepopen is not meant for use on the command line, but by
154 # keepopen is not meant for use on the command line, but by
154 # other extensions
155 # other extensions
155 self.keepopen = opts.get('keepopen', False)
156 self.keepopen = opts.get('keepopen', False)
156 self.obsoletenotrebased = {}
157 self.obsoletenotrebased = {}
157
158
158 def restorestatus(self):
159 def restorestatus(self):
159 """Restore a previously stored status"""
160 """Restore a previously stored status"""
160 repo = self.repo
161 repo = self.repo
161 keepbranches = None
162 keepbranches = None
162 target = None
163 target = None
163 collapse = False
164 collapse = False
164 external = nullrev
165 external = nullrev
165 activebookmark = None
166 activebookmark = None
166 state = {}
167 state = {}
167
168
168 try:
169 try:
169 f = repo.vfs("rebasestate")
170 f = repo.vfs("rebasestate")
170 for i, l in enumerate(f.read().splitlines()):
171 for i, l in enumerate(f.read().splitlines()):
171 if i == 0:
172 if i == 0:
172 originalwd = repo[l].rev()
173 originalwd = repo[l].rev()
173 elif i == 1:
174 elif i == 1:
174 target = repo[l].rev()
175 target = repo[l].rev()
175 elif i == 2:
176 elif i == 2:
176 external = repo[l].rev()
177 external = repo[l].rev()
177 elif i == 3:
178 elif i == 3:
178 collapse = bool(int(l))
179 collapse = bool(int(l))
179 elif i == 4:
180 elif i == 4:
180 keep = bool(int(l))
181 keep = bool(int(l))
181 elif i == 5:
182 elif i == 5:
182 keepbranches = bool(int(l))
183 keepbranches = bool(int(l))
183 elif i == 6 and not (len(l) == 81 and ':' in l):
184 elif i == 6 and not (len(l) == 81 and ':' in l):
184 # line 6 is a recent addition, so for backwards
185 # line 6 is a recent addition, so for backwards
185 # compatibility check that the line doesn't look like the
186 # compatibility check that the line doesn't look like the
186 # oldrev:newrev lines
187 # oldrev:newrev lines
187 activebookmark = l
188 activebookmark = l
188 else:
189 else:
189 oldrev, newrev = l.split(':')
190 oldrev, newrev = l.split(':')
190 if newrev in (str(nullmerge), str(revignored),
191 if newrev in (str(nullmerge), str(revignored),
191 str(revprecursor), str(revpruned)):
192 str(revprecursor), str(revpruned)):
192 state[repo[oldrev].rev()] = int(newrev)
193 state[repo[oldrev].rev()] = int(newrev)
193 elif newrev == nullid:
194 elif newrev == nullid:
194 state[repo[oldrev].rev()] = revtodo
195 state[repo[oldrev].rev()] = revtodo
195 # Legacy compat special case
196 # Legacy compat special case
196 else:
197 else:
197 state[repo[oldrev].rev()] = repo[newrev].rev()
198 state[repo[oldrev].rev()] = repo[newrev].rev()
198
199
199 except IOError as err:
200 except IOError as err:
200 if err.errno != errno.ENOENT:
201 if err.errno != errno.ENOENT:
201 raise
202 raise
202 cmdutil.wrongtooltocontinue(repo, _('rebase'))
203 cmdutil.wrongtooltocontinue(repo, _('rebase'))
203
204
204 if keepbranches is None:
205 if keepbranches is None:
205 raise error.Abort(_('.hg/rebasestate is incomplete'))
206 raise error.Abort(_('.hg/rebasestate is incomplete'))
206
207
207 skipped = set()
208 skipped = set()
208 # recompute the set of skipped revs
209 # recompute the set of skipped revs
209 if not collapse:
210 if not collapse:
210 seen = set([target])
211 seen = set([target])
211 for old, new in sorted(state.items()):
212 for old, new in sorted(state.items()):
212 if new != revtodo and new in seen:
213 if new != revtodo and new in seen:
213 skipped.add(old)
214 skipped.add(old)
214 seen.add(new)
215 seen.add(new)
215 repo.ui.debug('computed skipped revs: %s\n' %
216 repo.ui.debug('computed skipped revs: %s\n' %
216 (' '.join(str(r) for r in sorted(skipped)) or None))
217 (' '.join(str(r) for r in sorted(skipped)) or None))
217 repo.ui.debug('rebase status resumed\n')
218 repo.ui.debug('rebase status resumed\n')
218 _setrebasesetvisibility(repo, state.keys())
219 _setrebasesetvisibility(repo, state.keys())
219
220
220 self.originalwd = originalwd
221 self.originalwd = originalwd
221 self.target = target
222 self.target = target
222 self.state = state
223 self.state = state
223 self.skipped = skipped
224 self.skipped = skipped
224 self.collapsef = collapse
225 self.collapsef = collapse
225 self.keepf = keep
226 self.keepf = keep
226 self.keepbranchesf = keepbranches
227 self.keepbranchesf = keepbranches
227 self.external = external
228 self.external = external
228 self.activebookmark = activebookmark
229 self.activebookmark = activebookmark
229
230
230 def _prepareabortorcontinue(self, isabort):
231 def _prepareabortorcontinue(self, isabort):
231 try:
232 try:
232 self.restorestatus()
233 self.restorestatus()
233 self.collapsemsg = restorecollapsemsg(self.repo)
234 self.collapsemsg = restorecollapsemsg(self.repo)
234 except error.RepoLookupError:
235 except error.RepoLookupError:
235 if isabort:
236 if isabort:
236 clearstatus(self.repo)
237 clearstatus(self.repo)
237 clearcollapsemsg(self.repo)
238 clearcollapsemsg(self.repo)
238 self.repo.ui.warn(_('rebase aborted (no revision is removed,'
239 self.repo.ui.warn(_('rebase aborted (no revision is removed,'
239 ' only broken state is cleared)\n'))
240 ' only broken state is cleared)\n'))
240 return 0
241 return 0
241 else:
242 else:
242 msg = _('cannot continue inconsistent rebase')
243 msg = _('cannot continue inconsistent rebase')
243 hint = _('use "hg rebase --abort" to clear broken state')
244 hint = _('use "hg rebase --abort" to clear broken state')
244 raise error.Abort(msg, hint=hint)
245 raise error.Abort(msg, hint=hint)
245 if isabort:
246 if isabort:
246 return abort(self.repo, self.originalwd, self.target,
247 return abort(self.repo, self.originalwd, self.target,
247 self.state, activebookmark=self.activebookmark)
248 self.state, activebookmark=self.activebookmark)
248
249
249 self.obsoletenotrebased = {}
250 self.obsoletenotrebased = {}
250 if self.ui.configbool('experimental', 'rebaseskipobsolete',
251 if self.ui.configbool('experimental', 'rebaseskipobsolete',
251 default=True):
252 default=True):
252 rebaseobsrevs = set([r for r, st in self.state.items()
253 rebaseobsrevs = set([r for r, st in self.state.items()
253 if st == revprecursor])
254 if st == revprecursor])
254 rebasesetrevs = set(self.state.keys())
255 rebasesetrevs = set(self.state.keys())
255 self.obsoletenotrebased = _computeobsoletenotrebased(self.repo,
256 self.obsoletenotrebased = _computeobsoletenotrebased(self.repo,
256 rebaseobsrevs, self.target)
257 rebaseobsrevs, self.target)
257 rebaseobsskipped = set(self.obsoletenotrebased)
258 rebaseobsskipped = set(self.obsoletenotrebased)
258 _checkobsrebase(self.repo, self.ui, rebaseobsrevs, rebasesetrevs,
259 _checkobsrebase(self.repo, self.ui, rebaseobsrevs, rebasesetrevs,
259 rebaseobsskipped)
260 rebaseobsskipped)
260
261
261 def _preparenewrebase(self, dest, rebaseset):
262 def _preparenewrebase(self, dest, rebaseset):
262 if dest is None:
263 if dest is None:
263 return _nothingtorebase()
264 return _nothingtorebase()
264
265
265 allowunstable = obsolete.isenabled(self.repo, obsolete.allowunstableopt)
266 allowunstable = obsolete.isenabled(self.repo, obsolete.allowunstableopt)
266 if (not (self.keepf or allowunstable)
267 if (not (self.keepf or allowunstable)
267 and self.repo.revs('first(children(%ld) - %ld)',
268 and self.repo.revs('first(children(%ld) - %ld)',
268 rebaseset, rebaseset)):
269 rebaseset, rebaseset)):
269 raise error.Abort(
270 raise error.Abort(
270 _("can't remove original changesets with"
271 _("can't remove original changesets with"
271 " unrebased descendants"),
272 " unrebased descendants"),
272 hint=_('use --keep to keep original changesets'))
273 hint=_('use --keep to keep original changesets'))
273
274
274 self.obsoletenotrebased = {}
275 self.obsoletenotrebased = {}
275 if self.ui.configbool('experimental', 'rebaseskipobsolete',
276 if self.ui.configbool('experimental', 'rebaseskipobsolete',
276 default=True):
277 default=True):
277 rebasesetrevs = set(rebaseset)
278 rebasesetrevs = set(rebaseset)
278 rebaseobsrevs = _filterobsoleterevs(self.repo, rebasesetrevs)
279 rebaseobsrevs = _filterobsoleterevs(self.repo, rebasesetrevs)
279 self.obsoletenotrebased = _computeobsoletenotrebased(self.repo,
280 self.obsoletenotrebased = _computeobsoletenotrebased(self.repo,
280 rebaseobsrevs,
281 rebaseobsrevs,
281 dest)
282 dest)
282 rebaseobsskipped = set(self.obsoletenotrebased)
283 rebaseobsskipped = set(self.obsoletenotrebased)
283 _checkobsrebase(self.repo, self.ui, rebaseobsrevs,
284 _checkobsrebase(self.repo, self.ui, rebaseobsrevs,
284 rebasesetrevs,
285 rebasesetrevs,
285 rebaseobsskipped)
286 rebaseobsskipped)
286
287
287 result = buildstate(self.repo, dest, rebaseset, self.collapsef,
288 result = buildstate(self.repo, dest, rebaseset, self.collapsef,
288 self.obsoletenotrebased)
289 self.obsoletenotrebased)
289
290
290 if not result:
291 if not result:
291 # Empty state built, nothing to rebase
292 # Empty state built, nothing to rebase
292 self.ui.status(_('nothing to rebase\n'))
293 self.ui.status(_('nothing to rebase\n'))
293 return _nothingtorebase()
294 return _nothingtorebase()
294
295
295 root = min(rebaseset)
296 root = min(rebaseset)
296 if not self.keepf and not self.repo[root].mutable():
297 if not self.keepf and not self.repo[root].mutable():
297 raise error.Abort(_("can't rebase public changeset %s")
298 raise error.Abort(_("can't rebase public changeset %s")
298 % self.repo[root],
299 % self.repo[root],
299 hint=_('see "hg help phases" for details'))
300 hint=_('see "hg help phases" for details'))
300
301
301 (self.originalwd, self.target, self.state) = result
302 (self.originalwd, self.target, self.state) = result
302 if self.collapsef:
303 if self.collapsef:
303 self.targetancestors = self.repo.changelog.ancestors(
304 self.targetancestors = self.repo.changelog.ancestors(
304 [self.target],
305 [self.target],
305 inclusive=True)
306 inclusive=True)
306 self.external = externalparent(self.repo, self.state,
307 self.external = externalparent(self.repo, self.state,
307 self.targetancestors)
308 self.targetancestors)
308
309
309 if dest.closesbranch() and not self.keepbranchesf:
310 if dest.closesbranch() and not self.keepbranchesf:
310 self.ui.status(_('reopening closed branch head %s\n') % dest)
311 self.ui.status(_('reopening closed branch head %s\n') % dest)
311
312
312 @command('rebase',
313 @command('rebase',
313 [('s', 'source', '',
314 [('s', 'source', '',
314 _('rebase the specified changeset and descendants'), _('REV')),
315 _('rebase the specified changeset and descendants'), _('REV')),
315 ('b', 'base', '',
316 ('b', 'base', '',
316 _('rebase everything from branching point of specified changeset'),
317 _('rebase everything from branching point of specified changeset'),
317 _('REV')),
318 _('REV')),
318 ('r', 'rev', [],
319 ('r', 'rev', [],
319 _('rebase these revisions'),
320 _('rebase these revisions'),
320 _('REV')),
321 _('REV')),
321 ('d', 'dest', '',
322 ('d', 'dest', '',
322 _('rebase onto the specified changeset'), _('REV')),
323 _('rebase onto the specified changeset'), _('REV')),
323 ('', 'collapse', False, _('collapse the rebased changesets')),
324 ('', 'collapse', False, _('collapse the rebased changesets')),
324 ('m', 'message', '',
325 ('m', 'message', '',
325 _('use text as collapse commit message'), _('TEXT')),
326 _('use text as collapse commit message'), _('TEXT')),
326 ('e', 'edit', False, _('invoke editor on commit messages')),
327 ('e', 'edit', False, _('invoke editor on commit messages')),
327 ('l', 'logfile', '',
328 ('l', 'logfile', '',
328 _('read collapse commit message from file'), _('FILE')),
329 _('read collapse commit message from file'), _('FILE')),
329 ('k', 'keep', False, _('keep original changesets')),
330 ('k', 'keep', False, _('keep original changesets')),
330 ('', 'keepbranches', False, _('keep original branch names')),
331 ('', 'keepbranches', False, _('keep original branch names')),
331 ('D', 'detach', False, _('(DEPRECATED)')),
332 ('D', 'detach', False, _('(DEPRECATED)')),
332 ('i', 'interactive', False, _('(DEPRECATED)')),
333 ('i', 'interactive', False, _('(DEPRECATED)')),
333 ('t', 'tool', '', _('specify merge tool')),
334 ('t', 'tool', '', _('specify merge tool')),
334 ('c', 'continue', False, _('continue an interrupted rebase')),
335 ('c', 'continue', False, _('continue an interrupted rebase')),
335 ('a', 'abort', False, _('abort an interrupted rebase'))] +
336 ('a', 'abort', False, _('abort an interrupted rebase'))] +
336 templateopts,
337 templateopts,
337 _('[-s REV | -b REV] [-d REV] [OPTION]'))
338 _('[-s REV | -b REV] [-d REV] [OPTION]'))
338 def rebase(ui, repo, **opts):
339 def rebase(ui, repo, **opts):
339 """move changeset (and descendants) to a different branch
340 """move changeset (and descendants) to a different branch
340
341
341 Rebase uses repeated merging to graft changesets from one part of
342 Rebase uses repeated merging to graft changesets from one part of
342 history (the source) onto another (the destination). This can be
343 history (the source) onto another (the destination). This can be
343 useful for linearizing *local* changes relative to a master
344 useful for linearizing *local* changes relative to a master
344 development tree.
345 development tree.
345
346
346 Published commits cannot be rebased (see :hg:`help phases`).
347 Published commits cannot be rebased (see :hg:`help phases`).
347 To copy commits, see :hg:`help graft`.
348 To copy commits, see :hg:`help graft`.
348
349
349 If you don't specify a destination changeset (``-d/--dest``), rebase
350 If you don't specify a destination changeset (``-d/--dest``), rebase
350 will use the same logic as :hg:`merge` to pick a destination. if
351 will use the same logic as :hg:`merge` to pick a destination. if
351 the current branch contains exactly one other head, the other head
352 the current branch contains exactly one other head, the other head
352 is merged with by default. Otherwise, an explicit revision with
353 is merged with by default. Otherwise, an explicit revision with
353 which to merge with must be provided. (destination changeset is not
354 which to merge with must be provided. (destination changeset is not
354 modified by rebasing, but new changesets are added as its
355 modified by rebasing, but new changesets are added as its
355 descendants.)
356 descendants.)
356
357
357 Here are the ways to select changesets:
358 Here are the ways to select changesets:
358
359
359 1. Explicitly select them using ``--rev``.
360 1. Explicitly select them using ``--rev``.
360
361
361 2. Use ``--source`` to select a root changeset and include all of its
362 2. Use ``--source`` to select a root changeset and include all of its
362 descendants.
363 descendants.
363
364
364 3. Use ``--base`` to select a changeset; rebase will find ancestors
365 3. Use ``--base`` to select a changeset; rebase will find ancestors
365 and their descendants which are not also ancestors of the destination.
366 and their descendants which are not also ancestors of the destination.
366
367
367 4. If you do not specify any of ``--rev``, ``source``, or ``--base``,
368 4. If you do not specify any of ``--rev``, ``source``, or ``--base``,
368 rebase will use ``--base .`` as above.
369 rebase will use ``--base .`` as above.
369
370
370 Rebase will destroy original changesets unless you use ``--keep``.
371 Rebase will destroy original changesets unless you use ``--keep``.
371 It will also move your bookmarks (even if you do).
372 It will also move your bookmarks (even if you do).
372
373
373 Some changesets may be dropped if they do not contribute changes
374 Some changesets may be dropped if they do not contribute changes
374 (e.g. merges from the destination branch).
375 (e.g. merges from the destination branch).
375
376
376 Unlike ``merge``, rebase will do nothing if you are at the branch tip of
377 Unlike ``merge``, rebase will do nothing if you are at the branch tip of
377 a named branch with two heads. You will need to explicitly specify source
378 a named branch with two heads. You will need to explicitly specify source
378 and/or destination.
379 and/or destination.
379
380
380 If you need to use a tool to automate merge/conflict decisions, you
381 If you need to use a tool to automate merge/conflict decisions, you
381 can specify one with ``--tool``, see :hg:`help merge-tools`.
382 can specify one with ``--tool``, see :hg:`help merge-tools`.
382 As a caveat: the tool will not be used to mediate when a file was
383 As a caveat: the tool will not be used to mediate when a file was
383 deleted, there is no hook presently available for this.
384 deleted, there is no hook presently available for this.
384
385
385 If a rebase is interrupted to manually resolve a conflict, it can be
386 If a rebase is interrupted to manually resolve a conflict, it can be
386 continued with --continue/-c or aborted with --abort/-a.
387 continued with --continue/-c or aborted with --abort/-a.
387
388
388 .. container:: verbose
389 .. container:: verbose
389
390
390 Examples:
391 Examples:
391
392
392 - move "local changes" (current commit back to branching point)
393 - move "local changes" (current commit back to branching point)
393 to the current branch tip after a pull::
394 to the current branch tip after a pull::
394
395
395 hg rebase
396 hg rebase
396
397
397 - move a single changeset to the stable branch::
398 - move a single changeset to the stable branch::
398
399
399 hg rebase -r 5f493448 -d stable
400 hg rebase -r 5f493448 -d stable
400
401
401 - splice a commit and all its descendants onto another part of history::
402 - splice a commit and all its descendants onto another part of history::
402
403
403 hg rebase --source c0c3 --dest 4cf9
404 hg rebase --source c0c3 --dest 4cf9
404
405
405 - rebase everything on a branch marked by a bookmark onto the
406 - rebase everything on a branch marked by a bookmark onto the
406 default branch::
407 default branch::
407
408
408 hg rebase --base myfeature --dest default
409 hg rebase --base myfeature --dest default
409
410
410 - collapse a sequence of changes into a single commit::
411 - collapse a sequence of changes into a single commit::
411
412
412 hg rebase --collapse -r 1520:1525 -d .
413 hg rebase --collapse -r 1520:1525 -d .
413
414
414 - move a named branch while preserving its name::
415 - move a named branch while preserving its name::
415
416
416 hg rebase -r "branch(featureX)" -d 1.3 --keepbranches
417 hg rebase -r "branch(featureX)" -d 1.3 --keepbranches
417
418
418 Returns 0 on success, 1 if nothing to rebase or there are
419 Returns 0 on success, 1 if nothing to rebase or there are
419 unresolved conflicts.
420 unresolved conflicts.
420
421
421 """
422 """
422 rbsrt = rebaseruntime(repo, ui, opts)
423 rbsrt = rebaseruntime(repo, ui, opts)
423
424
424 lock = wlock = None
425 lock = wlock = None
425 try:
426 try:
426 wlock = repo.wlock()
427 wlock = repo.wlock()
427 lock = repo.lock()
428 lock = repo.lock()
428
429
429 # Validate input and define rebasing points
430 # Validate input and define rebasing points
430 destf = opts.get('dest', None)
431 destf = opts.get('dest', None)
431 srcf = opts.get('source', None)
432 srcf = opts.get('source', None)
432 basef = opts.get('base', None)
433 basef = opts.get('base', None)
433 revf = opts.get('rev', [])
434 revf = opts.get('rev', [])
434 # search default destination in this space
435 # search default destination in this space
435 # used in the 'hg pull --rebase' case, see issue 5214.
436 # used in the 'hg pull --rebase' case, see issue 5214.
436 destspace = opts.get('_destspace')
437 destspace = opts.get('_destspace')
437 contf = opts.get('continue')
438 contf = opts.get('continue')
438 abortf = opts.get('abort')
439 abortf = opts.get('abort')
439 if opts.get('interactive'):
440 if opts.get('interactive'):
440 try:
441 try:
441 if extensions.find('histedit'):
442 if extensions.find('histedit'):
442 enablehistedit = ''
443 enablehistedit = ''
443 except KeyError:
444 except KeyError:
444 enablehistedit = " --config extensions.histedit="
445 enablehistedit = " --config extensions.histedit="
445 help = "hg%s help -e histedit" % enablehistedit
446 help = "hg%s help -e histedit" % enablehistedit
446 msg = _("interactive history editing is supported by the "
447 msg = _("interactive history editing is supported by the "
447 "'histedit' extension (see \"%s\")") % help
448 "'histedit' extension (see \"%s\")") % help
448 raise error.Abort(msg)
449 raise error.Abort(msg)
449
450
450 if rbsrt.collapsemsg and not rbsrt.collapsef:
451 if rbsrt.collapsemsg and not rbsrt.collapsef:
451 raise error.Abort(
452 raise error.Abort(
452 _('message can only be specified with collapse'))
453 _('message can only be specified with collapse'))
453
454
454 if contf or abortf:
455 if contf or abortf:
455 if contf and abortf:
456 if contf and abortf:
456 raise error.Abort(_('cannot use both abort and continue'))
457 raise error.Abort(_('cannot use both abort and continue'))
457 if rbsrt.collapsef:
458 if rbsrt.collapsef:
458 raise error.Abort(
459 raise error.Abort(
459 _('cannot use collapse with continue or abort'))
460 _('cannot use collapse with continue or abort'))
460 if srcf or basef or destf:
461 if srcf or basef or destf:
461 raise error.Abort(
462 raise error.Abort(
462 _('abort and continue do not allow specifying revisions'))
463 _('abort and continue do not allow specifying revisions'))
463 if abortf and opts.get('tool', False):
464 if abortf and opts.get('tool', False):
464 ui.warn(_('tool option will be ignored\n'))
465 ui.warn(_('tool option will be ignored\n'))
465
466
466 retcode = rbsrt._prepareabortorcontinue(abortf)
467 retcode = rbsrt._prepareabortorcontinue(abortf)
467 if retcode is not None:
468 if retcode is not None:
468 return retcode
469 return retcode
469 else:
470 else:
470 dest, rebaseset = _definesets(ui, repo, destf, srcf, basef, revf,
471 dest, rebaseset = _definesets(ui, repo, destf, srcf, basef, revf,
471 destspace=destspace)
472 destspace=destspace)
472 retcode = rbsrt._preparenewrebase(dest, rebaseset)
473 retcode = rbsrt._preparenewrebase(dest, rebaseset)
473 if retcode is not None:
474 if retcode is not None:
474 return retcode
475 return retcode
475
476
476 if rbsrt.keepbranchesf:
477 if rbsrt.keepbranchesf:
477 # insert _savebranch at the start of extrafns so if
478 # insert _savebranch at the start of extrafns so if
478 # there's a user-provided extrafn it can clobber branch if
479 # there's a user-provided extrafn it can clobber branch if
479 # desired
480 # desired
480 rbsrt.extrafns.insert(0, _savebranch)
481 rbsrt.extrafns.insert(0, _savebranch)
481 if rbsrt.collapsef:
482 if rbsrt.collapsef:
482 branches = set()
483 branches = set()
483 for rev in rbsrt.state:
484 for rev in rbsrt.state:
484 branches.add(repo[rev].branch())
485 branches.add(repo[rev].branch())
485 if len(branches) > 1:
486 if len(branches) > 1:
486 raise error.Abort(_('cannot collapse multiple named '
487 raise error.Abort(_('cannot collapse multiple named '
487 'branches'))
488 'branches'))
488
489
489 # Rebase
490 # Rebase
490 if not rbsrt.targetancestors:
491 if not rbsrt.targetancestors:
491 rbsrt.targetancestors = repo.changelog.ancestors([rbsrt.target],
492 rbsrt.targetancestors = repo.changelog.ancestors([rbsrt.target],
492 inclusive=True)
493 inclusive=True)
493
494
494 # Keep track of the current bookmarks in order to reset them later
495 # Keep track of the current bookmarks in order to reset them later
495 currentbookmarks = repo._bookmarks.copy()
496 rbsrt.currentbookmarks = repo._bookmarks.copy()
496 rbsrt.activebookmark = rbsrt.activebookmark or repo._activebookmark
497 rbsrt.activebookmark = rbsrt.activebookmark or repo._activebookmark
497 if rbsrt.activebookmark:
498 if rbsrt.activebookmark:
498 bookmarks.deactivate(repo)
499 bookmarks.deactivate(repo)
499
500
500 extrafn = _makeextrafn(rbsrt.extrafns)
501 extrafn = _makeextrafn(rbsrt.extrafns)
501
502
502 rbsrt.sortedstate = sorted(rbsrt.state)
503 rbsrt.sortedstate = sorted(rbsrt.state)
503 total = len(rbsrt.sortedstate)
504 total = len(rbsrt.sortedstate)
504 pos = 0
505 pos = 0
505 for rev in rbsrt.sortedstate:
506 for rev in rbsrt.sortedstate:
506 ctx = repo[rev]
507 ctx = repo[rev]
507 desc = '%d:%s "%s"' % (ctx.rev(), ctx,
508 desc = '%d:%s "%s"' % (ctx.rev(), ctx,
508 ctx.description().split('\n', 1)[0])
509 ctx.description().split('\n', 1)[0])
509 names = repo.nodetags(ctx.node()) + repo.nodebookmarks(ctx.node())
510 names = repo.nodetags(ctx.node()) + repo.nodebookmarks(ctx.node())
510 if names:
511 if names:
511 desc += ' (%s)' % ' '.join(names)
512 desc += ' (%s)' % ' '.join(names)
512 pos += 1
513 pos += 1
513 if rbsrt.state[rev] == revtodo:
514 if rbsrt.state[rev] == revtodo:
514 ui.status(_('rebasing %s\n') % desc)
515 ui.status(_('rebasing %s\n') % desc)
515 ui.progress(_("rebasing"), pos, ("%d:%s" % (rev, ctx)),
516 ui.progress(_("rebasing"), pos, ("%d:%s" % (rev, ctx)),
516 _('changesets'), total)
517 _('changesets'), total)
517 p1, p2, base = defineparents(repo, rev, rbsrt.target,
518 p1, p2, base = defineparents(repo, rev, rbsrt.target,
518 rbsrt.state,
519 rbsrt.state,
519 rbsrt.targetancestors,
520 rbsrt.targetancestors,
520 rbsrt.obsoletenotrebased)
521 rbsrt.obsoletenotrebased)
521 storestatus(repo, rbsrt.originalwd, rbsrt.target,
522 storestatus(repo, rbsrt.originalwd, rbsrt.target,
522 rbsrt.state, rbsrt.collapsef, rbsrt.keepf,
523 rbsrt.state, rbsrt.collapsef, rbsrt.keepf,
523 rbsrt.keepbranchesf, rbsrt.external,
524 rbsrt.keepbranchesf, rbsrt.external,
524 rbsrt.activebookmark)
525 rbsrt.activebookmark)
525 storecollapsemsg(repo, rbsrt.collapsemsg)
526 storecollapsemsg(repo, rbsrt.collapsemsg)
526 if len(repo[None].parents()) == 2:
527 if len(repo[None].parents()) == 2:
527 repo.ui.debug('resuming interrupted rebase\n')
528 repo.ui.debug('resuming interrupted rebase\n')
528 else:
529 else:
529 try:
530 try:
530 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
531 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
531 'rebase')
532 'rebase')
532 stats = rebasenode(repo, rev, p1, base, rbsrt.state,
533 stats = rebasenode(repo, rev, p1, base, rbsrt.state,
533 rbsrt.collapsef, rbsrt.target)
534 rbsrt.collapsef, rbsrt.target)
534 if stats and stats[3] > 0:
535 if stats and stats[3] > 0:
535 raise error.InterventionRequired(
536 raise error.InterventionRequired(
536 _('unresolved conflicts (see hg '
537 _('unresolved conflicts (see hg '
537 'resolve, then hg rebase --continue)'))
538 'resolve, then hg rebase --continue)'))
538 finally:
539 finally:
539 ui.setconfig('ui', 'forcemerge', '', 'rebase')
540 ui.setconfig('ui', 'forcemerge', '', 'rebase')
540 if not rbsrt.collapsef:
541 if not rbsrt.collapsef:
541 merging = p2 != nullrev
542 merging = p2 != nullrev
542 editform = cmdutil.mergeeditform(merging, 'rebase')
543 editform = cmdutil.mergeeditform(merging, 'rebase')
543 editor = cmdutil.getcommiteditor(editform=editform, **opts)
544 editor = cmdutil.getcommiteditor(editform=editform, **opts)
544 newnode = concludenode(repo, rev, p1, p2, extrafn=extrafn,
545 newnode = concludenode(repo, rev, p1, p2, extrafn=extrafn,
545 editor=editor,
546 editor=editor,
546 keepbranches=rbsrt.keepbranchesf,
547 keepbranches=rbsrt.keepbranchesf,
547 date=rbsrt.date)
548 date=rbsrt.date)
548 else:
549 else:
549 # Skip commit if we are collapsing
550 # Skip commit if we are collapsing
550 repo.dirstate.beginparentchange()
551 repo.dirstate.beginparentchange()
551 repo.setparents(repo[p1].node())
552 repo.setparents(repo[p1].node())
552 repo.dirstate.endparentchange()
553 repo.dirstate.endparentchange()
553 newnode = None
554 newnode = None
554 # Update the state
555 # Update the state
555 if newnode is not None:
556 if newnode is not None:
556 rbsrt.state[rev] = repo[newnode].rev()
557 rbsrt.state[rev] = repo[newnode].rev()
557 ui.debug('rebased as %s\n' % short(newnode))
558 ui.debug('rebased as %s\n' % short(newnode))
558 else:
559 else:
559 if not rbsrt.collapsef:
560 if not rbsrt.collapsef:
560 ui.warn(_('note: rebase of %d:%s created no changes '
561 ui.warn(_('note: rebase of %d:%s created no changes '
561 'to commit\n') % (rev, ctx))
562 'to commit\n') % (rev, ctx))
562 rbsrt.skipped.add(rev)
563 rbsrt.skipped.add(rev)
563 rbsrt.state[rev] = p1
564 rbsrt.state[rev] = p1
564 ui.debug('next revision set to %s\n' % p1)
565 ui.debug('next revision set to %s\n' % p1)
565 elif rbsrt.state[rev] == nullmerge:
566 elif rbsrt.state[rev] == nullmerge:
566 ui.debug('ignoring null merge rebase of %s\n' % rev)
567 ui.debug('ignoring null merge rebase of %s\n' % rev)
567 elif rbsrt.state[rev] == revignored:
568 elif rbsrt.state[rev] == revignored:
568 ui.status(_('not rebasing ignored %s\n') % desc)
569 ui.status(_('not rebasing ignored %s\n') % desc)
569 elif rbsrt.state[rev] == revprecursor:
570 elif rbsrt.state[rev] == revprecursor:
570 targetctx = repo[rbsrt.obsoletenotrebased[rev]]
571 targetctx = repo[rbsrt.obsoletenotrebased[rev]]
571 desctarget = '%d:%s "%s"' % (targetctx.rev(), targetctx,
572 desctarget = '%d:%s "%s"' % (targetctx.rev(), targetctx,
572 targetctx.description().split('\n', 1)[0])
573 targetctx.description().split('\n', 1)[0])
573 msg = _('note: not rebasing %s, already in destination as %s\n')
574 msg = _('note: not rebasing %s, already in destination as %s\n')
574 ui.status(msg % (desc, desctarget))
575 ui.status(msg % (desc, desctarget))
575 elif rbsrt.state[rev] == revpruned:
576 elif rbsrt.state[rev] == revpruned:
576 msg = _('note: not rebasing %s, it has no successor\n')
577 msg = _('note: not rebasing %s, it has no successor\n')
577 ui.status(msg % desc)
578 ui.status(msg % desc)
578 else:
579 else:
579 ui.status(_('already rebased %s as %s\n') %
580 ui.status(_('already rebased %s as %s\n') %
580 (desc, repo[rbsrt.state[rev]]))
581 (desc, repo[rbsrt.state[rev]]))
581
582
582 ui.progress(_('rebasing'), None)
583 ui.progress(_('rebasing'), None)
583 ui.note(_('rebase merging completed\n'))
584 ui.note(_('rebase merging completed\n'))
584
585
585 if rbsrt.collapsef and not rbsrt.keepopen:
586 if rbsrt.collapsef and not rbsrt.keepopen:
586 p1, p2, _base = defineparents(repo, min(rbsrt.state),
587 p1, p2, _base = defineparents(repo, min(rbsrt.state),
587 rbsrt.target, rbsrt.state,
588 rbsrt.target, rbsrt.state,
588 rbsrt.targetancestors,
589 rbsrt.targetancestors,
589 rbsrt.obsoletenotrebased)
590 rbsrt.obsoletenotrebased)
590 editopt = opts.get('edit')
591 editopt = opts.get('edit')
591 editform = 'rebase.collapse'
592 editform = 'rebase.collapse'
592 if rbsrt.collapsemsg:
593 if rbsrt.collapsemsg:
593 commitmsg = rbsrt.collapsemsg
594 commitmsg = rbsrt.collapsemsg
594 else:
595 else:
595 commitmsg = 'Collapsed revision'
596 commitmsg = 'Collapsed revision'
596 for rebased in rbsrt.state:
597 for rebased in rbsrt.state:
597 if rebased not in rbsrt.skipped and\
598 if rebased not in rbsrt.skipped and\
598 rbsrt.state[rebased] > nullmerge:
599 rbsrt.state[rebased] > nullmerge:
599 commitmsg += '\n* %s' % repo[rebased].description()
600 commitmsg += '\n* %s' % repo[rebased].description()
600 editopt = True
601 editopt = True
601 editor = cmdutil.getcommiteditor(edit=editopt, editform=editform)
602 editor = cmdutil.getcommiteditor(edit=editopt, editform=editform)
602 revtoreuse = rbsrt.sortedstate[-1]
603 revtoreuse = rbsrt.sortedstate[-1]
603 newnode = concludenode(repo, revtoreuse, p1, rbsrt.external,
604 newnode = concludenode(repo, revtoreuse, p1, rbsrt.external,
604 commitmsg=commitmsg,
605 commitmsg=commitmsg,
605 extrafn=extrafn, editor=editor,
606 extrafn=extrafn, editor=editor,
606 keepbranches=rbsrt.keepbranchesf,
607 keepbranches=rbsrt.keepbranchesf,
607 date=rbsrt.date)
608 date=rbsrt.date)
608 if newnode is None:
609 if newnode is None:
609 newrev = rbsrt.target
610 newrev = rbsrt.target
610 else:
611 else:
611 newrev = repo[newnode].rev()
612 newrev = repo[newnode].rev()
612 for oldrev in rbsrt.state.iterkeys():
613 for oldrev in rbsrt.state.iterkeys():
613 if rbsrt.state[oldrev] > nullmerge:
614 if rbsrt.state[oldrev] > nullmerge:
614 rbsrt.state[oldrev] = newrev
615 rbsrt.state[oldrev] = newrev
615
616
616 if 'qtip' in repo.tags():
617 if 'qtip' in repo.tags():
617 updatemq(repo, rbsrt.state, rbsrt.skipped, **opts)
618 updatemq(repo, rbsrt.state, rbsrt.skipped, **opts)
618
619
619 if currentbookmarks:
620 if rbsrt.currentbookmarks:
620 # Nodeids are needed to reset bookmarks
621 # Nodeids are needed to reset bookmarks
621 nstate = {}
622 nstate = {}
622 for k, v in rbsrt.state.iteritems():
623 for k, v in rbsrt.state.iteritems():
623 if v > nullmerge:
624 if v > nullmerge:
624 nstate[repo[k].node()] = repo[v].node()
625 nstate[repo[k].node()] = repo[v].node()
625 elif v == revprecursor:
626 elif v == revprecursor:
626 succ = rbsrt.obsoletenotrebased[k]
627 succ = rbsrt.obsoletenotrebased[k]
627 nstate[repo[k].node()] = repo[succ].node()
628 nstate[repo[k].node()] = repo[succ].node()
628 # XXX this is the same as dest.node() for the non-continue path --
629 # XXX this is the same as dest.node() for the non-continue path --
629 # this should probably be cleaned up
630 # this should probably be cleaned up
630 targetnode = repo[rbsrt.target].node()
631 targetnode = repo[rbsrt.target].node()
631
632
632 # restore original working directory
633 # restore original working directory
633 # (we do this before stripping)
634 # (we do this before stripping)
634 newwd = rbsrt.state.get(rbsrt.originalwd, rbsrt.originalwd)
635 newwd = rbsrt.state.get(rbsrt.originalwd, rbsrt.originalwd)
635 if newwd == revprecursor:
636 if newwd == revprecursor:
636 newwd = rbsrt.obsoletenotrebased[rbsrt.originalwd]
637 newwd = rbsrt.obsoletenotrebased[rbsrt.originalwd]
637 elif newwd < 0:
638 elif newwd < 0:
638 # original directory is a parent of rebase set root or ignored
639 # original directory is a parent of rebase set root or ignored
639 newwd = rbsrt.originalwd
640 newwd = rbsrt.originalwd
640 if newwd not in [c.rev() for c in repo[None].parents()]:
641 if newwd not in [c.rev() for c in repo[None].parents()]:
641 ui.note(_("update back to initial working directory parent\n"))
642 ui.note(_("update back to initial working directory parent\n"))
642 hg.updaterepo(repo, newwd, False)
643 hg.updaterepo(repo, newwd, False)
643
644
644 if not rbsrt.keepf:
645 if not rbsrt.keepf:
645 collapsedas = None
646 collapsedas = None
646 if rbsrt.collapsef:
647 if rbsrt.collapsef:
647 collapsedas = newnode
648 collapsedas = newnode
648 clearrebased(ui, repo, rbsrt.state, rbsrt.skipped, collapsedas)
649 clearrebased(ui, repo, rbsrt.state, rbsrt.skipped, collapsedas)
649
650
650 with repo.transaction('bookmark') as tr:
651 with repo.transaction('bookmark') as tr:
651 if currentbookmarks:
652 if rbsrt.currentbookmarks:
652 updatebookmarks(repo, targetnode, nstate, currentbookmarks, tr)
653 updatebookmarks(repo, targetnode, nstate,
654 rbsrt.currentbookmarks, tr)
653 if rbsrt.activebookmark not in repo._bookmarks:
655 if rbsrt.activebookmark not in repo._bookmarks:
654 # active bookmark was divergent one and has been deleted
656 # active bookmark was divergent one and has been deleted
655 rbsrt.activebookmark = None
657 rbsrt.activebookmark = None
656 clearstatus(repo)
658 clearstatus(repo)
657 clearcollapsemsg(repo)
659 clearcollapsemsg(repo)
658
660
659 ui.note(_("rebase completed\n"))
661 ui.note(_("rebase completed\n"))
660 util.unlinkpath(repo.sjoin('undo'), ignoremissing=True)
662 util.unlinkpath(repo.sjoin('undo'), ignoremissing=True)
661 if rbsrt.skipped:
663 if rbsrt.skipped:
662 skippedlen = len(rbsrt.skipped)
664 skippedlen = len(rbsrt.skipped)
663 ui.note(_("%d revisions have been skipped\n") % skippedlen)
665 ui.note(_("%d revisions have been skipped\n") % skippedlen)
664
666
665 if (rbsrt.activebookmark and
667 if (rbsrt.activebookmark and
666 repo['.'].node() == repo._bookmarks[rbsrt.activebookmark]):
668 repo['.'].node() == repo._bookmarks[rbsrt.activebookmark]):
667 bookmarks.activate(repo, rbsrt.activebookmark)
669 bookmarks.activate(repo, rbsrt.activebookmark)
668
670
669 finally:
671 finally:
670 release(lock, wlock)
672 release(lock, wlock)
671
673
672 def _definesets(ui, repo, destf=None, srcf=None, basef=None, revf=[],
674 def _definesets(ui, repo, destf=None, srcf=None, basef=None, revf=[],
673 destspace=None):
675 destspace=None):
674 """use revisions argument to define destination and rebase set
676 """use revisions argument to define destination and rebase set
675 """
677 """
676 # destspace is here to work around issues with `hg pull --rebase` see
678 # destspace is here to work around issues with `hg pull --rebase` see
677 # issue5214 for details
679 # issue5214 for details
678 if srcf and basef:
680 if srcf and basef:
679 raise error.Abort(_('cannot specify both a source and a base'))
681 raise error.Abort(_('cannot specify both a source and a base'))
680 if revf and basef:
682 if revf and basef:
681 raise error.Abort(_('cannot specify both a revision and a base'))
683 raise error.Abort(_('cannot specify both a revision and a base'))
682 if revf and srcf:
684 if revf and srcf:
683 raise error.Abort(_('cannot specify both a revision and a source'))
685 raise error.Abort(_('cannot specify both a revision and a source'))
684
686
685 cmdutil.checkunfinished(repo)
687 cmdutil.checkunfinished(repo)
686 cmdutil.bailifchanged(repo)
688 cmdutil.bailifchanged(repo)
687
689
688 if destf:
690 if destf:
689 dest = scmutil.revsingle(repo, destf)
691 dest = scmutil.revsingle(repo, destf)
690
692
691 if revf:
693 if revf:
692 rebaseset = scmutil.revrange(repo, revf)
694 rebaseset = scmutil.revrange(repo, revf)
693 if not rebaseset:
695 if not rebaseset:
694 ui.status(_('empty "rev" revision set - nothing to rebase\n'))
696 ui.status(_('empty "rev" revision set - nothing to rebase\n'))
695 return None, None
697 return None, None
696 elif srcf:
698 elif srcf:
697 src = scmutil.revrange(repo, [srcf])
699 src = scmutil.revrange(repo, [srcf])
698 if not src:
700 if not src:
699 ui.status(_('empty "source" revision set - nothing to rebase\n'))
701 ui.status(_('empty "source" revision set - nothing to rebase\n'))
700 return None, None
702 return None, None
701 rebaseset = repo.revs('(%ld)::', src)
703 rebaseset = repo.revs('(%ld)::', src)
702 assert rebaseset
704 assert rebaseset
703 else:
705 else:
704 base = scmutil.revrange(repo, [basef or '.'])
706 base = scmutil.revrange(repo, [basef or '.'])
705 if not base:
707 if not base:
706 ui.status(_('empty "base" revision set - '
708 ui.status(_('empty "base" revision set - '
707 "can't compute rebase set\n"))
709 "can't compute rebase set\n"))
708 return None, None
710 return None, None
709 if not destf:
711 if not destf:
710 dest = repo[_destrebase(repo, base, destspace=destspace)]
712 dest = repo[_destrebase(repo, base, destspace=destspace)]
711 destf = str(dest)
713 destf = str(dest)
712
714
713 commonanc = repo.revs('ancestor(%ld, %d)', base, dest).first()
715 commonanc = repo.revs('ancestor(%ld, %d)', base, dest).first()
714 if commonanc is not None:
716 if commonanc is not None:
715 rebaseset = repo.revs('(%d::(%ld) - %d)::',
717 rebaseset = repo.revs('(%d::(%ld) - %d)::',
716 commonanc, base, commonanc)
718 commonanc, base, commonanc)
717 else:
719 else:
718 rebaseset = []
720 rebaseset = []
719
721
720 if not rebaseset:
722 if not rebaseset:
721 # transform to list because smartsets are not comparable to
723 # transform to list because smartsets are not comparable to
722 # lists. This should be improved to honor laziness of
724 # lists. This should be improved to honor laziness of
723 # smartset.
725 # smartset.
724 if list(base) == [dest.rev()]:
726 if list(base) == [dest.rev()]:
725 if basef:
727 if basef:
726 ui.status(_('nothing to rebase - %s is both "base"'
728 ui.status(_('nothing to rebase - %s is both "base"'
727 ' and destination\n') % dest)
729 ' and destination\n') % dest)
728 else:
730 else:
729 ui.status(_('nothing to rebase - working directory '
731 ui.status(_('nothing to rebase - working directory '
730 'parent is also destination\n'))
732 'parent is also destination\n'))
731 elif not repo.revs('%ld - ::%d', base, dest):
733 elif not repo.revs('%ld - ::%d', base, dest):
732 if basef:
734 if basef:
733 ui.status(_('nothing to rebase - "base" %s is '
735 ui.status(_('nothing to rebase - "base" %s is '
734 'already an ancestor of destination '
736 'already an ancestor of destination '
735 '%s\n') %
737 '%s\n') %
736 ('+'.join(str(repo[r]) for r in base),
738 ('+'.join(str(repo[r]) for r in base),
737 dest))
739 dest))
738 else:
740 else:
739 ui.status(_('nothing to rebase - working '
741 ui.status(_('nothing to rebase - working '
740 'directory parent is already an '
742 'directory parent is already an '
741 'ancestor of destination %s\n') % dest)
743 'ancestor of destination %s\n') % dest)
742 else: # can it happen?
744 else: # can it happen?
743 ui.status(_('nothing to rebase from %s to %s\n') %
745 ui.status(_('nothing to rebase from %s to %s\n') %
744 ('+'.join(str(repo[r]) for r in base), dest))
746 ('+'.join(str(repo[r]) for r in base), dest))
745 return None, None
747 return None, None
746
748
747 if not destf:
749 if not destf:
748 dest = repo[_destrebase(repo, rebaseset, destspace=destspace)]
750 dest = repo[_destrebase(repo, rebaseset, destspace=destspace)]
749 destf = str(dest)
751 destf = str(dest)
750
752
751 return dest, rebaseset
753 return dest, rebaseset
752
754
753 def externalparent(repo, state, targetancestors):
755 def externalparent(repo, state, targetancestors):
754 """Return the revision that should be used as the second parent
756 """Return the revision that should be used as the second parent
755 when the revisions in state is collapsed on top of targetancestors.
757 when the revisions in state is collapsed on top of targetancestors.
756 Abort if there is more than one parent.
758 Abort if there is more than one parent.
757 """
759 """
758 parents = set()
760 parents = set()
759 source = min(state)
761 source = min(state)
760 for rev in state:
762 for rev in state:
761 if rev == source:
763 if rev == source:
762 continue
764 continue
763 for p in repo[rev].parents():
765 for p in repo[rev].parents():
764 if (p.rev() not in state
766 if (p.rev() not in state
765 and p.rev() not in targetancestors):
767 and p.rev() not in targetancestors):
766 parents.add(p.rev())
768 parents.add(p.rev())
767 if not parents:
769 if not parents:
768 return nullrev
770 return nullrev
769 if len(parents) == 1:
771 if len(parents) == 1:
770 return parents.pop()
772 return parents.pop()
771 raise error.Abort(_('unable to collapse on top of %s, there is more '
773 raise error.Abort(_('unable to collapse on top of %s, there is more '
772 'than one external parent: %s') %
774 'than one external parent: %s') %
773 (max(targetancestors),
775 (max(targetancestors),
774 ', '.join(str(p) for p in sorted(parents))))
776 ', '.join(str(p) for p in sorted(parents))))
775
777
776 def concludenode(repo, rev, p1, p2, commitmsg=None, editor=None, extrafn=None,
778 def concludenode(repo, rev, p1, p2, commitmsg=None, editor=None, extrafn=None,
777 keepbranches=False, date=None):
779 keepbranches=False, date=None):
778 '''Commit the wd changes with parents p1 and p2. Reuse commit info from rev
780 '''Commit the wd changes with parents p1 and p2. Reuse commit info from rev
779 but also store useful information in extra.
781 but also store useful information in extra.
780 Return node of committed revision.'''
782 Return node of committed revision.'''
781 dsguard = cmdutil.dirstateguard(repo, 'rebase')
783 dsguard = cmdutil.dirstateguard(repo, 'rebase')
782 try:
784 try:
783 repo.setparents(repo[p1].node(), repo[p2].node())
785 repo.setparents(repo[p1].node(), repo[p2].node())
784 ctx = repo[rev]
786 ctx = repo[rev]
785 if commitmsg is None:
787 if commitmsg is None:
786 commitmsg = ctx.description()
788 commitmsg = ctx.description()
787 keepbranch = keepbranches and repo[p1].branch() != ctx.branch()
789 keepbranch = keepbranches and repo[p1].branch() != ctx.branch()
788 extra = {'rebase_source': ctx.hex()}
790 extra = {'rebase_source': ctx.hex()}
789 if extrafn:
791 if extrafn:
790 extrafn(ctx, extra)
792 extrafn(ctx, extra)
791
793
792 backup = repo.ui.backupconfig('phases', 'new-commit')
794 backup = repo.ui.backupconfig('phases', 'new-commit')
793 try:
795 try:
794 targetphase = max(ctx.phase(), phases.draft)
796 targetphase = max(ctx.phase(), phases.draft)
795 repo.ui.setconfig('phases', 'new-commit', targetphase, 'rebase')
797 repo.ui.setconfig('phases', 'new-commit', targetphase, 'rebase')
796 if keepbranch:
798 if keepbranch:
797 repo.ui.setconfig('ui', 'allowemptycommit', True)
799 repo.ui.setconfig('ui', 'allowemptycommit', True)
798 # Commit might fail if unresolved files exist
800 # Commit might fail if unresolved files exist
799 if date is None:
801 if date is None:
800 date = ctx.date()
802 date = ctx.date()
801 newnode = repo.commit(text=commitmsg, user=ctx.user(),
803 newnode = repo.commit(text=commitmsg, user=ctx.user(),
802 date=date, extra=extra, editor=editor)
804 date=date, extra=extra, editor=editor)
803 finally:
805 finally:
804 repo.ui.restoreconfig(backup)
806 repo.ui.restoreconfig(backup)
805
807
806 repo.dirstate.setbranch(repo[newnode].branch())
808 repo.dirstate.setbranch(repo[newnode].branch())
807 dsguard.close()
809 dsguard.close()
808 return newnode
810 return newnode
809 finally:
811 finally:
810 release(dsguard)
812 release(dsguard)
811
813
812 def rebasenode(repo, rev, p1, base, state, collapse, target):
814 def rebasenode(repo, rev, p1, base, state, collapse, target):
813 'Rebase a single revision rev on top of p1 using base as merge ancestor'
815 'Rebase a single revision rev on top of p1 using base as merge ancestor'
814 # Merge phase
816 # Merge phase
815 # Update to target and merge it with local
817 # Update to target and merge it with local
816 if repo['.'].rev() != p1:
818 if repo['.'].rev() != p1:
817 repo.ui.debug(" update to %d:%s\n" % (p1, repo[p1]))
819 repo.ui.debug(" update to %d:%s\n" % (p1, repo[p1]))
818 merge.update(repo, p1, False, True)
820 merge.update(repo, p1, False, True)
819 else:
821 else:
820 repo.ui.debug(" already in target\n")
822 repo.ui.debug(" already in target\n")
821 repo.dirstate.write(repo.currenttransaction())
823 repo.dirstate.write(repo.currenttransaction())
822 repo.ui.debug(" merge against %d:%s\n" % (rev, repo[rev]))
824 repo.ui.debug(" merge against %d:%s\n" % (rev, repo[rev]))
823 if base is not None:
825 if base is not None:
824 repo.ui.debug(" detach base %d:%s\n" % (base, repo[base]))
826 repo.ui.debug(" detach base %d:%s\n" % (base, repo[base]))
825 # When collapsing in-place, the parent is the common ancestor, we
827 # When collapsing in-place, the parent is the common ancestor, we
826 # have to allow merging with it.
828 # have to allow merging with it.
827 stats = merge.update(repo, rev, True, True, base, collapse,
829 stats = merge.update(repo, rev, True, True, base, collapse,
828 labels=['dest', 'source'])
830 labels=['dest', 'source'])
829 if collapse:
831 if collapse:
830 copies.duplicatecopies(repo, rev, target)
832 copies.duplicatecopies(repo, rev, target)
831 else:
833 else:
832 # If we're not using --collapse, we need to
834 # If we're not using --collapse, we need to
833 # duplicate copies between the revision we're
835 # duplicate copies between the revision we're
834 # rebasing and its first parent, but *not*
836 # rebasing and its first parent, but *not*
835 # duplicate any copies that have already been
837 # duplicate any copies that have already been
836 # performed in the destination.
838 # performed in the destination.
837 p1rev = repo[rev].p1().rev()
839 p1rev = repo[rev].p1().rev()
838 copies.duplicatecopies(repo, rev, p1rev, skiprev=target)
840 copies.duplicatecopies(repo, rev, p1rev, skiprev=target)
839 return stats
841 return stats
840
842
841 def nearestrebased(repo, rev, state):
843 def nearestrebased(repo, rev, state):
842 """return the nearest ancestors of rev in the rebase result"""
844 """return the nearest ancestors of rev in the rebase result"""
843 rebased = [r for r in state if state[r] > nullmerge]
845 rebased = [r for r in state if state[r] > nullmerge]
844 candidates = repo.revs('max(%ld and (::%d))', rebased, rev)
846 candidates = repo.revs('max(%ld and (::%d))', rebased, rev)
845 if candidates:
847 if candidates:
846 return state[candidates.first()]
848 return state[candidates.first()]
847 else:
849 else:
848 return None
850 return None
849
851
850 def _checkobsrebase(repo, ui,
852 def _checkobsrebase(repo, ui,
851 rebaseobsrevs,
853 rebaseobsrevs,
852 rebasesetrevs,
854 rebasesetrevs,
853 rebaseobsskipped):
855 rebaseobsskipped):
854 """
856 """
855 Abort if rebase will create divergence or rebase is noop because of markers
857 Abort if rebase will create divergence or rebase is noop because of markers
856
858
857 `rebaseobsrevs`: set of obsolete revision in source
859 `rebaseobsrevs`: set of obsolete revision in source
858 `rebasesetrevs`: set of revisions to be rebased from source
860 `rebasesetrevs`: set of revisions to be rebased from source
859 `rebaseobsskipped`: set of revisions from source skipped because they have
861 `rebaseobsskipped`: set of revisions from source skipped because they have
860 successors in destination
862 successors in destination
861 """
863 """
862 # Obsolete node with successors not in dest leads to divergence
864 # Obsolete node with successors not in dest leads to divergence
863 divergenceok = ui.configbool('experimental',
865 divergenceok = ui.configbool('experimental',
864 'allowdivergence')
866 'allowdivergence')
865 divergencebasecandidates = rebaseobsrevs - rebaseobsskipped
867 divergencebasecandidates = rebaseobsrevs - rebaseobsskipped
866
868
867 if divergencebasecandidates and not divergenceok:
869 if divergencebasecandidates and not divergenceok:
868 divhashes = (str(repo[r])
870 divhashes = (str(repo[r])
869 for r in divergencebasecandidates)
871 for r in divergencebasecandidates)
870 msg = _("this rebase will cause "
872 msg = _("this rebase will cause "
871 "divergences from: %s")
873 "divergences from: %s")
872 h = _("to force the rebase please set "
874 h = _("to force the rebase please set "
873 "experimental.allowdivergence=True")
875 "experimental.allowdivergence=True")
874 raise error.Abort(msg % (",".join(divhashes),), hint=h)
876 raise error.Abort(msg % (",".join(divhashes),), hint=h)
875
877
876 def defineparents(repo, rev, target, state, targetancestors,
878 def defineparents(repo, rev, target, state, targetancestors,
877 obsoletenotrebased):
879 obsoletenotrebased):
878 'Return the new parent relationship of the revision that will be rebased'
880 'Return the new parent relationship of the revision that will be rebased'
879 parents = repo[rev].parents()
881 parents = repo[rev].parents()
880 p1 = p2 = nullrev
882 p1 = p2 = nullrev
881 rp1 = None
883 rp1 = None
882
884
883 p1n = parents[0].rev()
885 p1n = parents[0].rev()
884 if p1n in targetancestors:
886 if p1n in targetancestors:
885 p1 = target
887 p1 = target
886 elif p1n in state:
888 elif p1n in state:
887 if state[p1n] == nullmerge:
889 if state[p1n] == nullmerge:
888 p1 = target
890 p1 = target
889 elif state[p1n] in revskipped:
891 elif state[p1n] in revskipped:
890 p1 = nearestrebased(repo, p1n, state)
892 p1 = nearestrebased(repo, p1n, state)
891 if p1 is None:
893 if p1 is None:
892 p1 = target
894 p1 = target
893 else:
895 else:
894 p1 = state[p1n]
896 p1 = state[p1n]
895 else: # p1n external
897 else: # p1n external
896 p1 = target
898 p1 = target
897 p2 = p1n
899 p2 = p1n
898
900
899 if len(parents) == 2 and parents[1].rev() not in targetancestors:
901 if len(parents) == 2 and parents[1].rev() not in targetancestors:
900 p2n = parents[1].rev()
902 p2n = parents[1].rev()
901 # interesting second parent
903 # interesting second parent
902 if p2n in state:
904 if p2n in state:
903 if p1 == target: # p1n in targetancestors or external
905 if p1 == target: # p1n in targetancestors or external
904 p1 = state[p2n]
906 p1 = state[p2n]
905 if p1 == revprecursor:
907 if p1 == revprecursor:
906 rp1 = obsoletenotrebased[p2n]
908 rp1 = obsoletenotrebased[p2n]
907 elif state[p2n] in revskipped:
909 elif state[p2n] in revskipped:
908 p2 = nearestrebased(repo, p2n, state)
910 p2 = nearestrebased(repo, p2n, state)
909 if p2 is None:
911 if p2 is None:
910 # no ancestors rebased yet, detach
912 # no ancestors rebased yet, detach
911 p2 = target
913 p2 = target
912 else:
914 else:
913 p2 = state[p2n]
915 p2 = state[p2n]
914 else: # p2n external
916 else: # p2n external
915 if p2 != nullrev: # p1n external too => rev is a merged revision
917 if p2 != nullrev: # p1n external too => rev is a merged revision
916 raise error.Abort(_('cannot use revision %d as base, result '
918 raise error.Abort(_('cannot use revision %d as base, result '
917 'would have 3 parents') % rev)
919 'would have 3 parents') % rev)
918 p2 = p2n
920 p2 = p2n
919 repo.ui.debug(" future parents are %d and %d\n" %
921 repo.ui.debug(" future parents are %d and %d\n" %
920 (repo[rp1 or p1].rev(), repo[p2].rev()))
922 (repo[rp1 or p1].rev(), repo[p2].rev()))
921
923
922 if not any(p.rev() in state for p in parents):
924 if not any(p.rev() in state for p in parents):
923 # Case (1) root changeset of a non-detaching rebase set.
925 # Case (1) root changeset of a non-detaching rebase set.
924 # Let the merge mechanism find the base itself.
926 # Let the merge mechanism find the base itself.
925 base = None
927 base = None
926 elif not repo[rev].p2():
928 elif not repo[rev].p2():
927 # Case (2) detaching the node with a single parent, use this parent
929 # Case (2) detaching the node with a single parent, use this parent
928 base = repo[rev].p1().rev()
930 base = repo[rev].p1().rev()
929 else:
931 else:
930 # Assuming there is a p1, this is the case where there also is a p2.
932 # Assuming there is a p1, this is the case where there also is a p2.
931 # We are thus rebasing a merge and need to pick the right merge base.
933 # We are thus rebasing a merge and need to pick the right merge base.
932 #
934 #
933 # Imagine we have:
935 # Imagine we have:
934 # - M: current rebase revision in this step
936 # - M: current rebase revision in this step
935 # - A: one parent of M
937 # - A: one parent of M
936 # - B: other parent of M
938 # - B: other parent of M
937 # - D: destination of this merge step (p1 var)
939 # - D: destination of this merge step (p1 var)
938 #
940 #
939 # Consider the case where D is a descendant of A or B and the other is
941 # Consider the case where D is a descendant of A or B and the other is
940 # 'outside'. In this case, the right merge base is the D ancestor.
942 # 'outside'. In this case, the right merge base is the D ancestor.
941 #
943 #
942 # An informal proof, assuming A is 'outside' and B is the D ancestor:
944 # An informal proof, assuming A is 'outside' and B is the D ancestor:
943 #
945 #
944 # If we pick B as the base, the merge involves:
946 # If we pick B as the base, the merge involves:
945 # - changes from B to M (actual changeset payload)
947 # - changes from B to M (actual changeset payload)
946 # - changes from B to D (induced by rebase) as D is a rebased
948 # - changes from B to D (induced by rebase) as D is a rebased
947 # version of B)
949 # version of B)
948 # Which exactly represent the rebase operation.
950 # Which exactly represent the rebase operation.
949 #
951 #
950 # If we pick A as the base, the merge involves:
952 # If we pick A as the base, the merge involves:
951 # - changes from A to M (actual changeset payload)
953 # - changes from A to M (actual changeset payload)
952 # - changes from A to D (with include changes between unrelated A and B
954 # - changes from A to D (with include changes between unrelated A and B
953 # plus changes induced by rebase)
955 # plus changes induced by rebase)
954 # Which does not represent anything sensible and creates a lot of
956 # Which does not represent anything sensible and creates a lot of
955 # conflicts. A is thus not the right choice - B is.
957 # conflicts. A is thus not the right choice - B is.
956 #
958 #
957 # Note: The base found in this 'proof' is only correct in the specified
959 # Note: The base found in this 'proof' is only correct in the specified
958 # case. This base does not make sense if is not D a descendant of A or B
960 # case. This base does not make sense if is not D a descendant of A or B
959 # or if the other is not parent 'outside' (especially not if the other
961 # or if the other is not parent 'outside' (especially not if the other
960 # parent has been rebased). The current implementation does not
962 # parent has been rebased). The current implementation does not
961 # make it feasible to consider different cases separately. In these
963 # make it feasible to consider different cases separately. In these
962 # other cases we currently just leave it to the user to correctly
964 # other cases we currently just leave it to the user to correctly
963 # resolve an impossible merge using a wrong ancestor.
965 # resolve an impossible merge using a wrong ancestor.
964 #
966 #
965 # xx, p1 could be -4, and both parents could probably be -4...
967 # xx, p1 could be -4, and both parents could probably be -4...
966 for p in repo[rev].parents():
968 for p in repo[rev].parents():
967 if state.get(p.rev()) == p1:
969 if state.get(p.rev()) == p1:
968 base = p.rev()
970 base = p.rev()
969 break
971 break
970 else: # fallback when base not found
972 else: # fallback when base not found
971 base = None
973 base = None
972
974
973 # Raise because this function is called wrong (see issue 4106)
975 # Raise because this function is called wrong (see issue 4106)
974 raise AssertionError('no base found to rebase on '
976 raise AssertionError('no base found to rebase on '
975 '(defineparents called wrong)')
977 '(defineparents called wrong)')
976 return rp1 or p1, p2, base
978 return rp1 or p1, p2, base
977
979
978 def isagitpatch(repo, patchname):
980 def isagitpatch(repo, patchname):
979 'Return true if the given patch is in git format'
981 'Return true if the given patch is in git format'
980 mqpatch = os.path.join(repo.mq.path, patchname)
982 mqpatch = os.path.join(repo.mq.path, patchname)
981 for line in patch.linereader(file(mqpatch, 'rb')):
983 for line in patch.linereader(file(mqpatch, 'rb')):
982 if line.startswith('diff --git'):
984 if line.startswith('diff --git'):
983 return True
985 return True
984 return False
986 return False
985
987
986 def updatemq(repo, state, skipped, **opts):
988 def updatemq(repo, state, skipped, **opts):
987 'Update rebased mq patches - finalize and then import them'
989 'Update rebased mq patches - finalize and then import them'
988 mqrebase = {}
990 mqrebase = {}
989 mq = repo.mq
991 mq = repo.mq
990 original_series = mq.fullseries[:]
992 original_series = mq.fullseries[:]
991 skippedpatches = set()
993 skippedpatches = set()
992
994
993 for p in mq.applied:
995 for p in mq.applied:
994 rev = repo[p.node].rev()
996 rev = repo[p.node].rev()
995 if rev in state:
997 if rev in state:
996 repo.ui.debug('revision %d is an mq patch (%s), finalize it.\n' %
998 repo.ui.debug('revision %d is an mq patch (%s), finalize it.\n' %
997 (rev, p.name))
999 (rev, p.name))
998 mqrebase[rev] = (p.name, isagitpatch(repo, p.name))
1000 mqrebase[rev] = (p.name, isagitpatch(repo, p.name))
999 else:
1001 else:
1000 # Applied but not rebased, not sure this should happen
1002 # Applied but not rebased, not sure this should happen
1001 skippedpatches.add(p.name)
1003 skippedpatches.add(p.name)
1002
1004
1003 if mqrebase:
1005 if mqrebase:
1004 mq.finish(repo, mqrebase.keys())
1006 mq.finish(repo, mqrebase.keys())
1005
1007
1006 # We must start import from the newest revision
1008 # We must start import from the newest revision
1007 for rev in sorted(mqrebase, reverse=True):
1009 for rev in sorted(mqrebase, reverse=True):
1008 if rev not in skipped:
1010 if rev not in skipped:
1009 name, isgit = mqrebase[rev]
1011 name, isgit = mqrebase[rev]
1010 repo.ui.note(_('updating mq patch %s to %s:%s\n') %
1012 repo.ui.note(_('updating mq patch %s to %s:%s\n') %
1011 (name, state[rev], repo[state[rev]]))
1013 (name, state[rev], repo[state[rev]]))
1012 mq.qimport(repo, (), patchname=name, git=isgit,
1014 mq.qimport(repo, (), patchname=name, git=isgit,
1013 rev=[str(state[rev])])
1015 rev=[str(state[rev])])
1014 else:
1016 else:
1015 # Rebased and skipped
1017 # Rebased and skipped
1016 skippedpatches.add(mqrebase[rev][0])
1018 skippedpatches.add(mqrebase[rev][0])
1017
1019
1018 # Patches were either applied and rebased and imported in
1020 # Patches were either applied and rebased and imported in
1019 # order, applied and removed or unapplied. Discard the removed
1021 # order, applied and removed or unapplied. Discard the removed
1020 # ones while preserving the original series order and guards.
1022 # ones while preserving the original series order and guards.
1021 newseries = [s for s in original_series
1023 newseries = [s for s in original_series
1022 if mq.guard_re.split(s, 1)[0] not in skippedpatches]
1024 if mq.guard_re.split(s, 1)[0] not in skippedpatches]
1023 mq.fullseries[:] = newseries
1025 mq.fullseries[:] = newseries
1024 mq.seriesdirty = True
1026 mq.seriesdirty = True
1025 mq.savedirty()
1027 mq.savedirty()
1026
1028
1027 def updatebookmarks(repo, targetnode, nstate, originalbookmarks, tr):
1029 def updatebookmarks(repo, targetnode, nstate, originalbookmarks, tr):
1028 'Move bookmarks to their correct changesets, and delete divergent ones'
1030 'Move bookmarks to their correct changesets, and delete divergent ones'
1029 marks = repo._bookmarks
1031 marks = repo._bookmarks
1030 for k, v in originalbookmarks.iteritems():
1032 for k, v in originalbookmarks.iteritems():
1031 if v in nstate:
1033 if v in nstate:
1032 # update the bookmarks for revs that have moved
1034 # update the bookmarks for revs that have moved
1033 marks[k] = nstate[v]
1035 marks[k] = nstate[v]
1034 bookmarks.deletedivergent(repo, [targetnode], k)
1036 bookmarks.deletedivergent(repo, [targetnode], k)
1035 marks.recordchange(tr)
1037 marks.recordchange(tr)
1036
1038
1037 def storecollapsemsg(repo, collapsemsg):
1039 def storecollapsemsg(repo, collapsemsg):
1038 'Store the collapse message to allow recovery'
1040 'Store the collapse message to allow recovery'
1039 collapsemsg = collapsemsg or ''
1041 collapsemsg = collapsemsg or ''
1040 f = repo.vfs("last-message.txt", "w")
1042 f = repo.vfs("last-message.txt", "w")
1041 f.write("%s\n" % collapsemsg)
1043 f.write("%s\n" % collapsemsg)
1042 f.close()
1044 f.close()
1043
1045
1044 def clearcollapsemsg(repo):
1046 def clearcollapsemsg(repo):
1045 'Remove collapse message file'
1047 'Remove collapse message file'
1046 util.unlinkpath(repo.join("last-message.txt"), ignoremissing=True)
1048 util.unlinkpath(repo.join("last-message.txt"), ignoremissing=True)
1047
1049
1048 def restorecollapsemsg(repo):
1050 def restorecollapsemsg(repo):
1049 'Restore previously stored collapse message'
1051 'Restore previously stored collapse message'
1050 try:
1052 try:
1051 f = repo.vfs("last-message.txt")
1053 f = repo.vfs("last-message.txt")
1052 collapsemsg = f.readline().strip()
1054 collapsemsg = f.readline().strip()
1053 f.close()
1055 f.close()
1054 except IOError as err:
1056 except IOError as err:
1055 if err.errno != errno.ENOENT:
1057 if err.errno != errno.ENOENT:
1056 raise
1058 raise
1057 raise error.Abort(_('no rebase in progress'))
1059 raise error.Abort(_('no rebase in progress'))
1058 return collapsemsg
1060 return collapsemsg
1059
1061
1060 def storestatus(repo, originalwd, target, state, collapse, keep, keepbranches,
1062 def storestatus(repo, originalwd, target, state, collapse, keep, keepbranches,
1061 external, activebookmark):
1063 external, activebookmark):
1062 'Store the current status to allow recovery'
1064 'Store the current status to allow recovery'
1063 f = repo.vfs("rebasestate", "w")
1065 f = repo.vfs("rebasestate", "w")
1064 f.write(repo[originalwd].hex() + '\n')
1066 f.write(repo[originalwd].hex() + '\n')
1065 f.write(repo[target].hex() + '\n')
1067 f.write(repo[target].hex() + '\n')
1066 f.write(repo[external].hex() + '\n')
1068 f.write(repo[external].hex() + '\n')
1067 f.write('%d\n' % int(collapse))
1069 f.write('%d\n' % int(collapse))
1068 f.write('%d\n' % int(keep))
1070 f.write('%d\n' % int(keep))
1069 f.write('%d\n' % int(keepbranches))
1071 f.write('%d\n' % int(keepbranches))
1070 f.write('%s\n' % (activebookmark or ''))
1072 f.write('%s\n' % (activebookmark or ''))
1071 for d, v in state.iteritems():
1073 for d, v in state.iteritems():
1072 oldrev = repo[d].hex()
1074 oldrev = repo[d].hex()
1073 if v >= 0:
1075 if v >= 0:
1074 newrev = repo[v].hex()
1076 newrev = repo[v].hex()
1075 elif v == revtodo:
1077 elif v == revtodo:
1076 # To maintain format compatibility, we have to use nullid.
1078 # To maintain format compatibility, we have to use nullid.
1077 # Please do remove this special case when upgrading the format.
1079 # Please do remove this special case when upgrading the format.
1078 newrev = hex(nullid)
1080 newrev = hex(nullid)
1079 else:
1081 else:
1080 newrev = v
1082 newrev = v
1081 f.write("%s:%s\n" % (oldrev, newrev))
1083 f.write("%s:%s\n" % (oldrev, newrev))
1082 f.close()
1084 f.close()
1083 repo.ui.debug('rebase status stored\n')
1085 repo.ui.debug('rebase status stored\n')
1084
1086
1085 def clearstatus(repo):
1087 def clearstatus(repo):
1086 'Remove the status files'
1088 'Remove the status files'
1087 _clearrebasesetvisibiliy(repo)
1089 _clearrebasesetvisibiliy(repo)
1088 util.unlinkpath(repo.join("rebasestate"), ignoremissing=True)
1090 util.unlinkpath(repo.join("rebasestate"), ignoremissing=True)
1089
1091
1090 def needupdate(repo, state):
1092 def needupdate(repo, state):
1091 '''check whether we should `update --clean` away from a merge, or if
1093 '''check whether we should `update --clean` away from a merge, or if
1092 somehow the working dir got forcibly updated, e.g. by older hg'''
1094 somehow the working dir got forcibly updated, e.g. by older hg'''
1093 parents = [p.rev() for p in repo[None].parents()]
1095 parents = [p.rev() for p in repo[None].parents()]
1094
1096
1095 # Are we in a merge state at all?
1097 # Are we in a merge state at all?
1096 if len(parents) < 2:
1098 if len(parents) < 2:
1097 return False
1099 return False
1098
1100
1099 # We should be standing on the first as-of-yet unrebased commit.
1101 # We should be standing on the first as-of-yet unrebased commit.
1100 firstunrebased = min([old for old, new in state.iteritems()
1102 firstunrebased = min([old for old, new in state.iteritems()
1101 if new == nullrev])
1103 if new == nullrev])
1102 if firstunrebased in parents:
1104 if firstunrebased in parents:
1103 return True
1105 return True
1104
1106
1105 return False
1107 return False
1106
1108
1107 def abort(repo, originalwd, target, state, activebookmark=None):
1109 def abort(repo, originalwd, target, state, activebookmark=None):
1108 '''Restore the repository to its original state. Additional args:
1110 '''Restore the repository to its original state. Additional args:
1109
1111
1110 activebookmark: the name of the bookmark that should be active after the
1112 activebookmark: the name of the bookmark that should be active after the
1111 restore'''
1113 restore'''
1112
1114
1113 try:
1115 try:
1114 # If the first commits in the rebased set get skipped during the rebase,
1116 # If the first commits in the rebased set get skipped during the rebase,
1115 # their values within the state mapping will be the target rev id. The
1117 # their values within the state mapping will be the target rev id. The
1116 # dstates list must must not contain the target rev (issue4896)
1118 # dstates list must must not contain the target rev (issue4896)
1117 dstates = [s for s in state.values() if s >= 0 and s != target]
1119 dstates = [s for s in state.values() if s >= 0 and s != target]
1118 immutable = [d for d in dstates if not repo[d].mutable()]
1120 immutable = [d for d in dstates if not repo[d].mutable()]
1119 cleanup = True
1121 cleanup = True
1120 if immutable:
1122 if immutable:
1121 repo.ui.warn(_("warning: can't clean up public changesets %s\n")
1123 repo.ui.warn(_("warning: can't clean up public changesets %s\n")
1122 % ', '.join(str(repo[r]) for r in immutable),
1124 % ', '.join(str(repo[r]) for r in immutable),
1123 hint=_('see "hg help phases" for details'))
1125 hint=_('see "hg help phases" for details'))
1124 cleanup = False
1126 cleanup = False
1125
1127
1126 descendants = set()
1128 descendants = set()
1127 if dstates:
1129 if dstates:
1128 descendants = set(repo.changelog.descendants(dstates))
1130 descendants = set(repo.changelog.descendants(dstates))
1129 if descendants - set(dstates):
1131 if descendants - set(dstates):
1130 repo.ui.warn(_("warning: new changesets detected on target branch, "
1132 repo.ui.warn(_("warning: new changesets detected on target branch, "
1131 "can't strip\n"))
1133 "can't strip\n"))
1132 cleanup = False
1134 cleanup = False
1133
1135
1134 if cleanup:
1136 if cleanup:
1135 shouldupdate = False
1137 shouldupdate = False
1136 rebased = filter(lambda x: x >= 0 and x != target, state.values())
1138 rebased = filter(lambda x: x >= 0 and x != target, state.values())
1137 if rebased:
1139 if rebased:
1138 strippoints = [
1140 strippoints = [
1139 c.node() for c in repo.set('roots(%ld)', rebased)]
1141 c.node() for c in repo.set('roots(%ld)', rebased)]
1140 shouldupdate = len([
1142 shouldupdate = len([
1141 c.node() for c in repo.set('. & (%ld)', rebased)]) > 0
1143 c.node() for c in repo.set('. & (%ld)', rebased)]) > 0
1142
1144
1143 # Update away from the rebase if necessary
1145 # Update away from the rebase if necessary
1144 if shouldupdate or needupdate(repo, state):
1146 if shouldupdate or needupdate(repo, state):
1145 merge.update(repo, originalwd, False, True)
1147 merge.update(repo, originalwd, False, True)
1146
1148
1147 # Strip from the first rebased revision
1149 # Strip from the first rebased revision
1148 if rebased:
1150 if rebased:
1149 # no backup of rebased cset versions needed
1151 # no backup of rebased cset versions needed
1150 repair.strip(repo.ui, repo, strippoints)
1152 repair.strip(repo.ui, repo, strippoints)
1151
1153
1152 if activebookmark and activebookmark in repo._bookmarks:
1154 if activebookmark and activebookmark in repo._bookmarks:
1153 bookmarks.activate(repo, activebookmark)
1155 bookmarks.activate(repo, activebookmark)
1154
1156
1155 finally:
1157 finally:
1156 clearstatus(repo)
1158 clearstatus(repo)
1157 clearcollapsemsg(repo)
1159 clearcollapsemsg(repo)
1158 repo.ui.warn(_('rebase aborted\n'))
1160 repo.ui.warn(_('rebase aborted\n'))
1159 return 0
1161 return 0
1160
1162
1161 def buildstate(repo, dest, rebaseset, collapse, obsoletenotrebased):
1163 def buildstate(repo, dest, rebaseset, collapse, obsoletenotrebased):
1162 '''Define which revisions are going to be rebased and where
1164 '''Define which revisions are going to be rebased and where
1163
1165
1164 repo: repo
1166 repo: repo
1165 dest: context
1167 dest: context
1166 rebaseset: set of rev
1168 rebaseset: set of rev
1167 '''
1169 '''
1168 _setrebasesetvisibility(repo, rebaseset)
1170 _setrebasesetvisibility(repo, rebaseset)
1169
1171
1170 # This check isn't strictly necessary, since mq detects commits over an
1172 # This check isn't strictly necessary, since mq detects commits over an
1171 # applied patch. But it prevents messing up the working directory when
1173 # applied patch. But it prevents messing up the working directory when
1172 # a partially completed rebase is blocked by mq.
1174 # a partially completed rebase is blocked by mq.
1173 if 'qtip' in repo.tags() and (dest.node() in
1175 if 'qtip' in repo.tags() and (dest.node() in
1174 [s.node for s in repo.mq.applied]):
1176 [s.node for s in repo.mq.applied]):
1175 raise error.Abort(_('cannot rebase onto an applied mq patch'))
1177 raise error.Abort(_('cannot rebase onto an applied mq patch'))
1176
1178
1177 roots = list(repo.set('roots(%ld)', rebaseset))
1179 roots = list(repo.set('roots(%ld)', rebaseset))
1178 if not roots:
1180 if not roots:
1179 raise error.Abort(_('no matching revisions'))
1181 raise error.Abort(_('no matching revisions'))
1180 roots.sort()
1182 roots.sort()
1181 state = {}
1183 state = {}
1182 detachset = set()
1184 detachset = set()
1183 for root in roots:
1185 for root in roots:
1184 commonbase = root.ancestor(dest)
1186 commonbase = root.ancestor(dest)
1185 if commonbase == root:
1187 if commonbase == root:
1186 raise error.Abort(_('source is ancestor of destination'))
1188 raise error.Abort(_('source is ancestor of destination'))
1187 if commonbase == dest:
1189 if commonbase == dest:
1188 samebranch = root.branch() == dest.branch()
1190 samebranch = root.branch() == dest.branch()
1189 if not collapse and samebranch and root in dest.children():
1191 if not collapse and samebranch and root in dest.children():
1190 repo.ui.debug('source is a child of destination\n')
1192 repo.ui.debug('source is a child of destination\n')
1191 return None
1193 return None
1192
1194
1193 repo.ui.debug('rebase onto %d starting from %s\n' % (dest, root))
1195 repo.ui.debug('rebase onto %d starting from %s\n' % (dest, root))
1194 state.update(dict.fromkeys(rebaseset, revtodo))
1196 state.update(dict.fromkeys(rebaseset, revtodo))
1195 # Rebase tries to turn <dest> into a parent of <root> while
1197 # Rebase tries to turn <dest> into a parent of <root> while
1196 # preserving the number of parents of rebased changesets:
1198 # preserving the number of parents of rebased changesets:
1197 #
1199 #
1198 # - A changeset with a single parent will always be rebased as a
1200 # - A changeset with a single parent will always be rebased as a
1199 # changeset with a single parent.
1201 # changeset with a single parent.
1200 #
1202 #
1201 # - A merge will be rebased as merge unless its parents are both
1203 # - A merge will be rebased as merge unless its parents are both
1202 # ancestors of <dest> or are themselves in the rebased set and
1204 # ancestors of <dest> or are themselves in the rebased set and
1203 # pruned while rebased.
1205 # pruned while rebased.
1204 #
1206 #
1205 # If one parent of <root> is an ancestor of <dest>, the rebased
1207 # If one parent of <root> is an ancestor of <dest>, the rebased
1206 # version of this parent will be <dest>. This is always true with
1208 # version of this parent will be <dest>. This is always true with
1207 # --base option.
1209 # --base option.
1208 #
1210 #
1209 # Otherwise, we need to *replace* the original parents with
1211 # Otherwise, we need to *replace* the original parents with
1210 # <dest>. This "detaches" the rebased set from its former location
1212 # <dest>. This "detaches" the rebased set from its former location
1211 # and rebases it onto <dest>. Changes introduced by ancestors of
1213 # and rebases it onto <dest>. Changes introduced by ancestors of
1212 # <root> not common with <dest> (the detachset, marked as
1214 # <root> not common with <dest> (the detachset, marked as
1213 # nullmerge) are "removed" from the rebased changesets.
1215 # nullmerge) are "removed" from the rebased changesets.
1214 #
1216 #
1215 # - If <root> has a single parent, set it to <dest>.
1217 # - If <root> has a single parent, set it to <dest>.
1216 #
1218 #
1217 # - If <root> is a merge, we cannot decide which parent to
1219 # - If <root> is a merge, we cannot decide which parent to
1218 # replace, the rebase operation is not clearly defined.
1220 # replace, the rebase operation is not clearly defined.
1219 #
1221 #
1220 # The table below sums up this behavior:
1222 # The table below sums up this behavior:
1221 #
1223 #
1222 # +------------------+----------------------+-------------------------+
1224 # +------------------+----------------------+-------------------------+
1223 # | | one parent | merge |
1225 # | | one parent | merge |
1224 # +------------------+----------------------+-------------------------+
1226 # +------------------+----------------------+-------------------------+
1225 # | parent in | new parent is <dest> | parents in ::<dest> are |
1227 # | parent in | new parent is <dest> | parents in ::<dest> are |
1226 # | ::<dest> | | remapped to <dest> |
1228 # | ::<dest> | | remapped to <dest> |
1227 # +------------------+----------------------+-------------------------+
1229 # +------------------+----------------------+-------------------------+
1228 # | unrelated source | new parent is <dest> | ambiguous, abort |
1230 # | unrelated source | new parent is <dest> | ambiguous, abort |
1229 # +------------------+----------------------+-------------------------+
1231 # +------------------+----------------------+-------------------------+
1230 #
1232 #
1231 # The actual abort is handled by `defineparents`
1233 # The actual abort is handled by `defineparents`
1232 if len(root.parents()) <= 1:
1234 if len(root.parents()) <= 1:
1233 # ancestors of <root> not ancestors of <dest>
1235 # ancestors of <root> not ancestors of <dest>
1234 detachset.update(repo.changelog.findmissingrevs([commonbase.rev()],
1236 detachset.update(repo.changelog.findmissingrevs([commonbase.rev()],
1235 [root.rev()]))
1237 [root.rev()]))
1236 for r in detachset:
1238 for r in detachset:
1237 if r not in state:
1239 if r not in state:
1238 state[r] = nullmerge
1240 state[r] = nullmerge
1239 if len(roots) > 1:
1241 if len(roots) > 1:
1240 # If we have multiple roots, we may have "hole" in the rebase set.
1242 # If we have multiple roots, we may have "hole" in the rebase set.
1241 # Rebase roots that descend from those "hole" should not be detached as
1243 # Rebase roots that descend from those "hole" should not be detached as
1242 # other root are. We use the special `revignored` to inform rebase that
1244 # other root are. We use the special `revignored` to inform rebase that
1243 # the revision should be ignored but that `defineparents` should search
1245 # the revision should be ignored but that `defineparents` should search
1244 # a rebase destination that make sense regarding rebased topology.
1246 # a rebase destination that make sense regarding rebased topology.
1245 rebasedomain = set(repo.revs('%ld::%ld', rebaseset, rebaseset))
1247 rebasedomain = set(repo.revs('%ld::%ld', rebaseset, rebaseset))
1246 for ignored in set(rebasedomain) - set(rebaseset):
1248 for ignored in set(rebasedomain) - set(rebaseset):
1247 state[ignored] = revignored
1249 state[ignored] = revignored
1248 for r in obsoletenotrebased:
1250 for r in obsoletenotrebased:
1249 if obsoletenotrebased[r] is None:
1251 if obsoletenotrebased[r] is None:
1250 state[r] = revpruned
1252 state[r] = revpruned
1251 else:
1253 else:
1252 state[r] = revprecursor
1254 state[r] = revprecursor
1253 return repo['.'].rev(), dest.rev(), state
1255 return repo['.'].rev(), dest.rev(), state
1254
1256
1255 def clearrebased(ui, repo, state, skipped, collapsedas=None):
1257 def clearrebased(ui, repo, state, skipped, collapsedas=None):
1256 """dispose of rebased revision at the end of the rebase
1258 """dispose of rebased revision at the end of the rebase
1257
1259
1258 If `collapsedas` is not None, the rebase was a collapse whose result if the
1260 If `collapsedas` is not None, the rebase was a collapse whose result if the
1259 `collapsedas` node."""
1261 `collapsedas` node."""
1260 if obsolete.isenabled(repo, obsolete.createmarkersopt):
1262 if obsolete.isenabled(repo, obsolete.createmarkersopt):
1261 markers = []
1263 markers = []
1262 for rev, newrev in sorted(state.items()):
1264 for rev, newrev in sorted(state.items()):
1263 if newrev >= 0:
1265 if newrev >= 0:
1264 if rev in skipped:
1266 if rev in skipped:
1265 succs = ()
1267 succs = ()
1266 elif collapsedas is not None:
1268 elif collapsedas is not None:
1267 succs = (repo[collapsedas],)
1269 succs = (repo[collapsedas],)
1268 else:
1270 else:
1269 succs = (repo[newrev],)
1271 succs = (repo[newrev],)
1270 markers.append((repo[rev], succs))
1272 markers.append((repo[rev], succs))
1271 if markers:
1273 if markers:
1272 obsolete.createmarkers(repo, markers)
1274 obsolete.createmarkers(repo, markers)
1273 else:
1275 else:
1274 rebased = [rev for rev in state if state[rev] > nullmerge]
1276 rebased = [rev for rev in state if state[rev] > nullmerge]
1275 if rebased:
1277 if rebased:
1276 stripped = []
1278 stripped = []
1277 for root in repo.set('roots(%ld)', rebased):
1279 for root in repo.set('roots(%ld)', rebased):
1278 if set(repo.changelog.descendants([root.rev()])) - set(state):
1280 if set(repo.changelog.descendants([root.rev()])) - set(state):
1279 ui.warn(_("warning: new changesets detected "
1281 ui.warn(_("warning: new changesets detected "
1280 "on source branch, not stripping\n"))
1282 "on source branch, not stripping\n"))
1281 else:
1283 else:
1282 stripped.append(root.node())
1284 stripped.append(root.node())
1283 if stripped:
1285 if stripped:
1284 # backup the old csets by default
1286 # backup the old csets by default
1285 repair.strip(ui, repo, stripped, "all")
1287 repair.strip(ui, repo, stripped, "all")
1286
1288
1287
1289
1288 def pullrebase(orig, ui, repo, *args, **opts):
1290 def pullrebase(orig, ui, repo, *args, **opts):
1289 'Call rebase after pull if the latter has been invoked with --rebase'
1291 'Call rebase after pull if the latter has been invoked with --rebase'
1290 ret = None
1292 ret = None
1291 if opts.get('rebase'):
1293 if opts.get('rebase'):
1292 wlock = lock = None
1294 wlock = lock = None
1293 try:
1295 try:
1294 wlock = repo.wlock()
1296 wlock = repo.wlock()
1295 lock = repo.lock()
1297 lock = repo.lock()
1296 if opts.get('update'):
1298 if opts.get('update'):
1297 del opts['update']
1299 del opts['update']
1298 ui.debug('--update and --rebase are not compatible, ignoring '
1300 ui.debug('--update and --rebase are not compatible, ignoring '
1299 'the update flag\n')
1301 'the update flag\n')
1300
1302
1301 revsprepull = len(repo)
1303 revsprepull = len(repo)
1302 origpostincoming = commands.postincoming
1304 origpostincoming = commands.postincoming
1303 def _dummy(*args, **kwargs):
1305 def _dummy(*args, **kwargs):
1304 pass
1306 pass
1305 commands.postincoming = _dummy
1307 commands.postincoming = _dummy
1306 try:
1308 try:
1307 ret = orig(ui, repo, *args, **opts)
1309 ret = orig(ui, repo, *args, **opts)
1308 finally:
1310 finally:
1309 commands.postincoming = origpostincoming
1311 commands.postincoming = origpostincoming
1310 revspostpull = len(repo)
1312 revspostpull = len(repo)
1311 if revspostpull > revsprepull:
1313 if revspostpull > revsprepull:
1312 # --rev option from pull conflict with rebase own --rev
1314 # --rev option from pull conflict with rebase own --rev
1313 # dropping it
1315 # dropping it
1314 if 'rev' in opts:
1316 if 'rev' in opts:
1315 del opts['rev']
1317 del opts['rev']
1316 # positional argument from pull conflicts with rebase's own
1318 # positional argument from pull conflicts with rebase's own
1317 # --source.
1319 # --source.
1318 if 'source' in opts:
1320 if 'source' in opts:
1319 del opts['source']
1321 del opts['source']
1320 # revsprepull is the len of the repo, not revnum of tip.
1322 # revsprepull is the len of the repo, not revnum of tip.
1321 destspace = list(repo.changelog.revs(start=revsprepull))
1323 destspace = list(repo.changelog.revs(start=revsprepull))
1322 opts['_destspace'] = destspace
1324 opts['_destspace'] = destspace
1323 try:
1325 try:
1324 rebase(ui, repo, **opts)
1326 rebase(ui, repo, **opts)
1325 except error.NoMergeDestAbort:
1327 except error.NoMergeDestAbort:
1326 # we can maybe update instead
1328 # we can maybe update instead
1327 rev, _a, _b = destutil.destupdate(repo)
1329 rev, _a, _b = destutil.destupdate(repo)
1328 if rev == repo['.'].rev():
1330 if rev == repo['.'].rev():
1329 ui.status(_('nothing to rebase\n'))
1331 ui.status(_('nothing to rebase\n'))
1330 else:
1332 else:
1331 ui.status(_('nothing to rebase - updating instead\n'))
1333 ui.status(_('nothing to rebase - updating instead\n'))
1332 # not passing argument to get the bare update behavior
1334 # not passing argument to get the bare update behavior
1333 # with warning and trumpets
1335 # with warning and trumpets
1334 commands.update(ui, repo)
1336 commands.update(ui, repo)
1335 finally:
1337 finally:
1336 release(lock, wlock)
1338 release(lock, wlock)
1337 else:
1339 else:
1338 if opts.get('tool'):
1340 if opts.get('tool'):
1339 raise error.Abort(_('--tool can only be used with --rebase'))
1341 raise error.Abort(_('--tool can only be used with --rebase'))
1340 ret = orig(ui, repo, *args, **opts)
1342 ret = orig(ui, repo, *args, **opts)
1341
1343
1342 return ret
1344 return ret
1343
1345
1344 def _setrebasesetvisibility(repo, revs):
1346 def _setrebasesetvisibility(repo, revs):
1345 """store the currently rebased set on the repo object
1347 """store the currently rebased set on the repo object
1346
1348
1347 This is used by another function to prevent rebased revision to because
1349 This is used by another function to prevent rebased revision to because
1348 hidden (see issue4505)"""
1350 hidden (see issue4505)"""
1349 repo = repo.unfiltered()
1351 repo = repo.unfiltered()
1350 revs = set(revs)
1352 revs = set(revs)
1351 repo._rebaseset = revs
1353 repo._rebaseset = revs
1352 # invalidate cache if visibility changes
1354 # invalidate cache if visibility changes
1353 hiddens = repo.filteredrevcache.get('visible', set())
1355 hiddens = repo.filteredrevcache.get('visible', set())
1354 if revs & hiddens:
1356 if revs & hiddens:
1355 repo.invalidatevolatilesets()
1357 repo.invalidatevolatilesets()
1356
1358
1357 def _clearrebasesetvisibiliy(repo):
1359 def _clearrebasesetvisibiliy(repo):
1358 """remove rebaseset data from the repo"""
1360 """remove rebaseset data from the repo"""
1359 repo = repo.unfiltered()
1361 repo = repo.unfiltered()
1360 if '_rebaseset' in vars(repo):
1362 if '_rebaseset' in vars(repo):
1361 del repo._rebaseset
1363 del repo._rebaseset
1362
1364
1363 def _rebasedvisible(orig, repo):
1365 def _rebasedvisible(orig, repo):
1364 """ensure rebased revs stay visible (see issue4505)"""
1366 """ensure rebased revs stay visible (see issue4505)"""
1365 blockers = orig(repo)
1367 blockers = orig(repo)
1366 blockers.update(getattr(repo, '_rebaseset', ()))
1368 blockers.update(getattr(repo, '_rebaseset', ()))
1367 return blockers
1369 return blockers
1368
1370
1369 def _filterobsoleterevs(repo, revs):
1371 def _filterobsoleterevs(repo, revs):
1370 """returns a set of the obsolete revisions in revs"""
1372 """returns a set of the obsolete revisions in revs"""
1371 return set(r for r in revs if repo[r].obsolete())
1373 return set(r for r in revs if repo[r].obsolete())
1372
1374
1373 def _computeobsoletenotrebased(repo, rebaseobsrevs, dest):
1375 def _computeobsoletenotrebased(repo, rebaseobsrevs, dest):
1374 """return a mapping obsolete => successor for all obsolete nodes to be
1376 """return a mapping obsolete => successor for all obsolete nodes to be
1375 rebased that have a successors in the destination
1377 rebased that have a successors in the destination
1376
1378
1377 obsolete => None entries in the mapping indicate nodes with no succesor"""
1379 obsolete => None entries in the mapping indicate nodes with no succesor"""
1378 obsoletenotrebased = {}
1380 obsoletenotrebased = {}
1379
1381
1380 # Build a mapping successor => obsolete nodes for the obsolete
1382 # Build a mapping successor => obsolete nodes for the obsolete
1381 # nodes to be rebased
1383 # nodes to be rebased
1382 allsuccessors = {}
1384 allsuccessors = {}
1383 cl = repo.changelog
1385 cl = repo.changelog
1384 for r in rebaseobsrevs:
1386 for r in rebaseobsrevs:
1385 node = cl.node(r)
1387 node = cl.node(r)
1386 for s in obsolete.allsuccessors(repo.obsstore, [node]):
1388 for s in obsolete.allsuccessors(repo.obsstore, [node]):
1387 try:
1389 try:
1388 allsuccessors[cl.rev(s)] = cl.rev(node)
1390 allsuccessors[cl.rev(s)] = cl.rev(node)
1389 except LookupError:
1391 except LookupError:
1390 pass
1392 pass
1391
1393
1392 if allsuccessors:
1394 if allsuccessors:
1393 # Look for successors of obsolete nodes to be rebased among
1395 # Look for successors of obsolete nodes to be rebased among
1394 # the ancestors of dest
1396 # the ancestors of dest
1395 ancs = cl.ancestors([repo[dest].rev()],
1397 ancs = cl.ancestors([repo[dest].rev()],
1396 stoprev=min(allsuccessors),
1398 stoprev=min(allsuccessors),
1397 inclusive=True)
1399 inclusive=True)
1398 for s in allsuccessors:
1400 for s in allsuccessors:
1399 if s in ancs:
1401 if s in ancs:
1400 obsoletenotrebased[allsuccessors[s]] = s
1402 obsoletenotrebased[allsuccessors[s]] = s
1401 elif (s == allsuccessors[s] and
1403 elif (s == allsuccessors[s] and
1402 allsuccessors.values().count(s) == 1):
1404 allsuccessors.values().count(s) == 1):
1403 # plain prune
1405 # plain prune
1404 obsoletenotrebased[s] = None
1406 obsoletenotrebased[s] = None
1405
1407
1406 return obsoletenotrebased
1408 return obsoletenotrebased
1407
1409
1408 def summaryhook(ui, repo):
1410 def summaryhook(ui, repo):
1409 if not os.path.exists(repo.join('rebasestate')):
1411 if not os.path.exists(repo.join('rebasestate')):
1410 return
1412 return
1411 try:
1413 try:
1412 rbsrt = rebaseruntime(repo, ui, {})
1414 rbsrt = rebaseruntime(repo, ui, {})
1413 rbsrt.restorestatus()
1415 rbsrt.restorestatus()
1414 state = rbsrt.state
1416 state = rbsrt.state
1415 except error.RepoLookupError:
1417 except error.RepoLookupError:
1416 # i18n: column positioning for "hg summary"
1418 # i18n: column positioning for "hg summary"
1417 msg = _('rebase: (use "hg rebase --abort" to clear broken state)\n')
1419 msg = _('rebase: (use "hg rebase --abort" to clear broken state)\n')
1418 ui.write(msg)
1420 ui.write(msg)
1419 return
1421 return
1420 numrebased = len([i for i in state.itervalues() if i >= 0])
1422 numrebased = len([i for i in state.itervalues() if i >= 0])
1421 # i18n: column positioning for "hg summary"
1423 # i18n: column positioning for "hg summary"
1422 ui.write(_('rebase: %s, %s (rebase --continue)\n') %
1424 ui.write(_('rebase: %s, %s (rebase --continue)\n') %
1423 (ui.label(_('%d rebased'), 'rebase.rebased') % numrebased,
1425 (ui.label(_('%d rebased'), 'rebase.rebased') % numrebased,
1424 ui.label(_('%d remaining'), 'rebase.remaining') %
1426 ui.label(_('%d remaining'), 'rebase.remaining') %
1425 (len(state) - numrebased)))
1427 (len(state) - numrebased)))
1426
1428
1427 def uisetup(ui):
1429 def uisetup(ui):
1428 #Replace pull with a decorator to provide --rebase option
1430 #Replace pull with a decorator to provide --rebase option
1429 entry = extensions.wrapcommand(commands.table, 'pull', pullrebase)
1431 entry = extensions.wrapcommand(commands.table, 'pull', pullrebase)
1430 entry[1].append(('', 'rebase', None,
1432 entry[1].append(('', 'rebase', None,
1431 _("rebase working directory to branch head")))
1433 _("rebase working directory to branch head")))
1432 entry[1].append(('t', 'tool', '',
1434 entry[1].append(('t', 'tool', '',
1433 _("specify merge tool for rebase")))
1435 _("specify merge tool for rebase")))
1434 cmdutil.summaryhooks.add('rebase', summaryhook)
1436 cmdutil.summaryhooks.add('rebase', summaryhook)
1435 cmdutil.unfinishedstates.append(
1437 cmdutil.unfinishedstates.append(
1436 ['rebasestate', False, False, _('rebase in progress'),
1438 ['rebasestate', False, False, _('rebase in progress'),
1437 _("use 'hg rebase --continue' or 'hg rebase --abort'")])
1439 _("use 'hg rebase --continue' or 'hg rebase --abort'")])
1438 cmdutil.afterresolvedstates.append(
1440 cmdutil.afterresolvedstates.append(
1439 ['rebasestate', _('hg rebase --continue')])
1441 ['rebasestate', _('hg rebase --continue')])
1440 # ensure rebased rev are not hidden
1442 # ensure rebased rev are not hidden
1441 extensions.wrapfunction(repoview, '_getdynamicblockers', _rebasedvisible)
1443 extensions.wrapfunction(repoview, '_getdynamicblockers', _rebasedvisible)
General Comments 0
You need to be logged in to leave comments. Login now