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