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