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