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