##// END OF EJS Templates
rebase: support multiple roots for rebaseset...
Pierre-Yves David -
r18424:100fdc84 default
parent child Browse files
Show More
@@ -1,733 +1,737 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 http://mercurial.selenic.com/wiki/RebaseExtension
14 http://mercurial.selenic.com/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
18 from mercurial import extensions, patch, scmutil, phases, obsolete
19 from mercurial.commands import templateopts
19 from mercurial.commands import templateopts
20 from mercurial.node import nullrev
20 from mercurial.node import nullrev
21 from mercurial.lock import release
21 from mercurial.lock import release
22 from mercurial.i18n import _
22 from mercurial.i18n import _
23 import os, errno
23 import os, errno
24
24
25 nullmerge = -2
25 nullmerge = -2
26
26
27 cmdtable = {}
27 cmdtable = {}
28 command = cmdutil.command(cmdtable)
28 command = cmdutil.command(cmdtable)
29 testedwith = 'internal'
29 testedwith = 'internal'
30
30
31 @command('rebase',
31 @command('rebase',
32 [('s', 'source', '',
32 [('s', 'source', '',
33 _('rebase from the specified changeset'), _('REV')),
33 _('rebase from the specified changeset'), _('REV')),
34 ('b', 'base', '',
34 ('b', 'base', '',
35 _('rebase from the base of the specified changeset '
35 _('rebase from the base of the specified changeset '
36 '(up to greatest common ancestor of base and dest)'),
36 '(up to greatest common ancestor of base and dest)'),
37 _('REV')),
37 _('REV')),
38 ('r', 'rev', [],
38 ('r', 'rev', [],
39 _('rebase these revisions'),
39 _('rebase these revisions'),
40 _('REV')),
40 _('REV')),
41 ('d', 'dest', '',
41 ('d', 'dest', '',
42 _('rebase onto the specified changeset'), _('REV')),
42 _('rebase onto the specified changeset'), _('REV')),
43 ('', 'collapse', False, _('collapse the rebased changesets')),
43 ('', 'collapse', False, _('collapse the rebased changesets')),
44 ('m', 'message', '',
44 ('m', 'message', '',
45 _('use text as collapse commit message'), _('TEXT')),
45 _('use text as collapse commit message'), _('TEXT')),
46 ('e', 'edit', False, _('invoke editor on commit messages')),
46 ('e', 'edit', False, _('invoke editor on commit messages')),
47 ('l', 'logfile', '',
47 ('l', 'logfile', '',
48 _('read collapse commit message from file'), _('FILE')),
48 _('read collapse commit message from file'), _('FILE')),
49 ('', 'keep', False, _('keep original changesets')),
49 ('', 'keep', False, _('keep original changesets')),
50 ('', 'keepbranches', False, _('keep original branch names')),
50 ('', 'keepbranches', False, _('keep original branch names')),
51 ('D', 'detach', False, _('(DEPRECATED)')),
51 ('D', 'detach', False, _('(DEPRECATED)')),
52 ('t', 'tool', '', _('specify merge tool')),
52 ('t', 'tool', '', _('specify merge tool')),
53 ('c', 'continue', False, _('continue an interrupted rebase')),
53 ('c', 'continue', False, _('continue an interrupted rebase')),
54 ('a', 'abort', False, _('abort an interrupted rebase'))] +
54 ('a', 'abort', False, _('abort an interrupted rebase'))] +
55 templateopts,
55 templateopts,
56 _('[-s REV | -b REV] [-d REV] [OPTION]'))
56 _('[-s REV | -b REV] [-d REV] [OPTION]'))
57 def rebase(ui, repo, **opts):
57 def rebase(ui, repo, **opts):
58 """move changeset (and descendants) to a different branch
58 """move changeset (and descendants) to a different branch
59
59
60 Rebase uses repeated merging to graft changesets from one part of
60 Rebase uses repeated merging to graft changesets from one part of
61 history (the source) onto another (the destination). This can be
61 history (the source) onto another (the destination). This can be
62 useful for linearizing *local* changes relative to a master
62 useful for linearizing *local* changes relative to a master
63 development tree.
63 development tree.
64
64
65 You should not rebase changesets that have already been shared
65 You should not rebase changesets that have already been shared
66 with others. Doing so will force everybody else to perform the
66 with others. Doing so will force everybody else to perform the
67 same rebase or they will end up with duplicated changesets after
67 same rebase or they will end up with duplicated changesets after
68 pulling in your rebased changesets.
68 pulling in your rebased changesets.
69
69
70 If you don't specify a destination changeset (``-d/--dest``),
70 If you don't specify a destination changeset (``-d/--dest``),
71 rebase uses the tipmost head of the current named branch as the
71 rebase uses the tipmost head of the current named branch as the
72 destination. (The destination changeset is not modified by
72 destination. (The destination changeset is not modified by
73 rebasing, but new changesets are added as its descendants.)
73 rebasing, but new changesets are added as its descendants.)
74
74
75 You can specify which changesets to rebase in two ways: as a
75 You can specify which changesets to rebase in two ways: as a
76 "source" changeset or as a "base" changeset. Both are shorthand
76 "source" changeset or as a "base" changeset. Both are shorthand
77 for a topologically related set of changesets (the "source
77 for a topologically related set of changesets (the "source
78 branch"). If you specify source (``-s/--source``), rebase will
78 branch"). If you specify source (``-s/--source``), rebase will
79 rebase that changeset and all of its descendants onto dest. If you
79 rebase that changeset and all of its descendants onto dest. If you
80 specify base (``-b/--base``), rebase will select ancestors of base
80 specify base (``-b/--base``), rebase will select ancestors of base
81 back to but not including the common ancestor with dest. Thus,
81 back to but not including the common ancestor with dest. Thus,
82 ``-b`` is less precise but more convenient than ``-s``: you can
82 ``-b`` is less precise but more convenient than ``-s``: you can
83 specify any changeset in the source branch, and rebase will select
83 specify any changeset in the source branch, and rebase will select
84 the whole branch. If you specify neither ``-s`` nor ``-b``, rebase
84 the whole branch. If you specify neither ``-s`` nor ``-b``, rebase
85 uses the parent of the working directory as the base.
85 uses the parent of the working directory as the base.
86
86
87 By default, rebase recreates the changesets in the source branch
87 By default, rebase recreates the changesets in the source branch
88 as descendants of dest and then destroys the originals. Use
88 as descendants of dest and then destroys the originals. Use
89 ``--keep`` to preserve the original source changesets. Some
89 ``--keep`` to preserve the original source changesets. Some
90 changesets in the source branch (e.g. merges from the destination
90 changesets in the source branch (e.g. merges from the destination
91 branch) may be dropped if they no longer contribute any change.
91 branch) may be dropped if they no longer contribute any change.
92
92
93 One result of the rules for selecting the destination changeset
93 One result of the rules for selecting the destination changeset
94 and source branch is that, unlike ``merge``, rebase will do
94 and source branch is that, unlike ``merge``, rebase will do
95 nothing if you are at the latest (tipmost) head of a named branch
95 nothing if you are at the latest (tipmost) head of a named branch
96 with two heads. You need to explicitly specify source and/or
96 with two heads. You need to explicitly specify source and/or
97 destination (or ``update`` to the other head, if it's the head of
97 destination (or ``update`` to the other head, if it's the head of
98 the intended source branch).
98 the intended source branch).
99
99
100 If a rebase is interrupted to manually resolve a merge, it can be
100 If a rebase is interrupted to manually resolve a merge, it can be
101 continued with --continue/-c or aborted with --abort/-a.
101 continued with --continue/-c or aborted with --abort/-a.
102
102
103 Returns 0 on success, 1 if nothing to rebase.
103 Returns 0 on success, 1 if nothing to rebase.
104 """
104 """
105 originalwd = target = None
105 originalwd = target = None
106 external = nullrev
106 external = nullrev
107 state = {}
107 state = {}
108 skipped = set()
108 skipped = set()
109 targetancestors = set()
109 targetancestors = set()
110
110
111 editor = None
111 editor = None
112 if opts.get('edit'):
112 if opts.get('edit'):
113 editor = cmdutil.commitforceeditor
113 editor = cmdutil.commitforceeditor
114
114
115 lock = wlock = None
115 lock = wlock = None
116 try:
116 try:
117 wlock = repo.wlock()
117 wlock = repo.wlock()
118 lock = repo.lock()
118 lock = repo.lock()
119
119
120 # Validate input and define rebasing points
120 # Validate input and define rebasing points
121 destf = opts.get('dest', None)
121 destf = opts.get('dest', None)
122 srcf = opts.get('source', None)
122 srcf = opts.get('source', None)
123 basef = opts.get('base', None)
123 basef = opts.get('base', None)
124 revf = opts.get('rev', [])
124 revf = opts.get('rev', [])
125 contf = opts.get('continue')
125 contf = opts.get('continue')
126 abortf = opts.get('abort')
126 abortf = opts.get('abort')
127 collapsef = opts.get('collapse', False)
127 collapsef = opts.get('collapse', False)
128 collapsemsg = cmdutil.logmessage(ui, opts)
128 collapsemsg = cmdutil.logmessage(ui, opts)
129 extrafn = opts.get('extrafn') # internal, used by e.g. hgsubversion
129 extrafn = opts.get('extrafn') # internal, used by e.g. hgsubversion
130 keepf = opts.get('keep', False)
130 keepf = opts.get('keep', False)
131 keepbranchesf = opts.get('keepbranches', False)
131 keepbranchesf = opts.get('keepbranches', False)
132 # keepopen is not meant for use on the command line, but by
132 # keepopen is not meant for use on the command line, but by
133 # other extensions
133 # other extensions
134 keepopen = opts.get('keepopen', False)
134 keepopen = opts.get('keepopen', False)
135
135
136 if collapsemsg and not collapsef:
136 if collapsemsg and not collapsef:
137 raise util.Abort(
137 raise util.Abort(
138 _('message can only be specified with collapse'))
138 _('message can only be specified with collapse'))
139
139
140 if contf or abortf:
140 if contf or abortf:
141 if contf and abortf:
141 if contf and abortf:
142 raise util.Abort(_('cannot use both abort and continue'))
142 raise util.Abort(_('cannot use both abort and continue'))
143 if collapsef:
143 if collapsef:
144 raise util.Abort(
144 raise util.Abort(
145 _('cannot use collapse with continue or abort'))
145 _('cannot use collapse with continue or abort'))
146 if srcf or basef or destf:
146 if srcf or basef or destf:
147 raise util.Abort(
147 raise util.Abort(
148 _('abort and continue do not allow specifying revisions'))
148 _('abort and continue do not allow specifying revisions'))
149 if opts.get('tool', False):
149 if opts.get('tool', False):
150 ui.warn(_('tool option will be ignored\n'))
150 ui.warn(_('tool option will be ignored\n'))
151
151
152 (originalwd, target, state, skipped, collapsef, keepf,
152 (originalwd, target, state, skipped, collapsef, keepf,
153 keepbranchesf, external) = restorestatus(repo)
153 keepbranchesf, external) = restorestatus(repo)
154 if abortf:
154 if abortf:
155 return abort(repo, originalwd, target, state)
155 return abort(repo, originalwd, target, state)
156 else:
156 else:
157 if srcf and basef:
157 if srcf and basef:
158 raise util.Abort(_('cannot specify both a '
158 raise util.Abort(_('cannot specify both a '
159 'source and a base'))
159 'source and a base'))
160 if revf and basef:
160 if revf and basef:
161 raise util.Abort(_('cannot specify both a '
161 raise util.Abort(_('cannot specify both a '
162 'revision and a base'))
162 'revision and a base'))
163 if revf and srcf:
163 if revf and srcf:
164 raise util.Abort(_('cannot specify both a '
164 raise util.Abort(_('cannot specify both a '
165 'revision and a source'))
165 'revision and a source'))
166
166
167 cmdutil.bailifchanged(repo)
167 cmdutil.bailifchanged(repo)
168
168
169 if not destf:
169 if not destf:
170 # Destination defaults to the latest revision in the
170 # Destination defaults to the latest revision in the
171 # current branch
171 # current branch
172 branch = repo[None].branch()
172 branch = repo[None].branch()
173 dest = repo[branch]
173 dest = repo[branch]
174 else:
174 else:
175 dest = scmutil.revsingle(repo, destf)
175 dest = scmutil.revsingle(repo, destf)
176
176
177 if revf:
177 if revf:
178 rebaseset = repo.revs('%lr', revf)
178 rebaseset = repo.revs('%lr', revf)
179 elif srcf:
179 elif srcf:
180 src = scmutil.revrange(repo, [srcf])
180 src = scmutil.revrange(repo, [srcf])
181 rebaseset = repo.revs('(%ld)::', src)
181 rebaseset = repo.revs('(%ld)::', src)
182 else:
182 else:
183 base = scmutil.revrange(repo, [basef or '.'])
183 base = scmutil.revrange(repo, [basef or '.'])
184 rebaseset = repo.revs(
184 rebaseset = repo.revs(
185 '(children(ancestor(%ld, %d)) and ::(%ld))::',
185 '(children(ancestor(%ld, %d)) and ::(%ld))::',
186 base, dest, base)
186 base, dest, base)
187 if rebaseset:
187 if rebaseset:
188 root = min(rebaseset)
188 root = min(rebaseset)
189 else:
189 else:
190 root = None
190 root = None
191
191
192 if not rebaseset:
192 if not rebaseset:
193 repo.ui.debug('base is ancestor of destination\n')
193 repo.ui.debug('base is ancestor of destination\n')
194 result = None
194 result = None
195 elif (not (keepf or obsolete._enabled)
195 elif (not (keepf or obsolete._enabled)
196 and repo.revs('first(children(%ld) - %ld)',
196 and repo.revs('first(children(%ld) - %ld)',
197 rebaseset, rebaseset)):
197 rebaseset, rebaseset)):
198 raise util.Abort(
198 raise util.Abort(
199 _("can't remove original changesets with"
199 _("can't remove original changesets with"
200 " unrebased descendants"),
200 " unrebased descendants"),
201 hint=_('use --keep to keep original changesets'))
201 hint=_('use --keep to keep original changesets'))
202 elif not keepf and not repo[root].mutable():
202 elif not keepf and not repo[root].mutable():
203 raise util.Abort(_("can't rebase immutable changeset %s")
203 raise util.Abort(_("can't rebase immutable changeset %s")
204 % repo[root],
204 % repo[root],
205 hint=_('see hg help phases for details'))
205 hint=_('see hg help phases for details'))
206 else:
206 else:
207 result = buildstate(repo, dest, rebaseset, collapsef)
207 result = buildstate(repo, dest, rebaseset, collapsef)
208
208
209 if not result:
209 if not result:
210 # Empty state built, nothing to rebase
210 # Empty state built, nothing to rebase
211 ui.status(_('nothing to rebase\n'))
211 ui.status(_('nothing to rebase\n'))
212 return 1
212 return 1
213 else:
213 else:
214 originalwd, target, state = result
214 originalwd, target, state = result
215 if collapsef:
215 if collapsef:
216 targetancestors = repo.changelog.ancestors([target],
216 targetancestors = repo.changelog.ancestors([target],
217 inclusive=True)
217 inclusive=True)
218 external = checkexternal(repo, state, targetancestors)
218 external = checkexternal(repo, state, targetancestors)
219
219
220 if keepbranchesf:
220 if keepbranchesf:
221 assert not extrafn, 'cannot use both keepbranches and extrafn'
221 assert not extrafn, 'cannot use both keepbranches and extrafn'
222 def extrafn(ctx, extra):
222 def extrafn(ctx, extra):
223 extra['branch'] = ctx.branch()
223 extra['branch'] = ctx.branch()
224 if collapsef:
224 if collapsef:
225 branches = set()
225 branches = set()
226 for rev in state:
226 for rev in state:
227 branches.add(repo[rev].branch())
227 branches.add(repo[rev].branch())
228 if len(branches) > 1:
228 if len(branches) > 1:
229 raise util.Abort(_('cannot collapse multiple named '
229 raise util.Abort(_('cannot collapse multiple named '
230 'branches'))
230 'branches'))
231
231
232
232
233 # Rebase
233 # Rebase
234 if not targetancestors:
234 if not targetancestors:
235 targetancestors = repo.changelog.ancestors([target], inclusive=True)
235 targetancestors = repo.changelog.ancestors([target], inclusive=True)
236
236
237 # Keep track of the current bookmarks in order to reset them later
237 # Keep track of the current bookmarks in order to reset them later
238 currentbookmarks = repo._bookmarks.copy()
238 currentbookmarks = repo._bookmarks.copy()
239 activebookmark = repo._bookmarkcurrent
239 activebookmark = repo._bookmarkcurrent
240 if activebookmark:
240 if activebookmark:
241 bookmarks.unsetcurrent(repo)
241 bookmarks.unsetcurrent(repo)
242
242
243 sortedstate = sorted(state)
243 sortedstate = sorted(state)
244 total = len(sortedstate)
244 total = len(sortedstate)
245 pos = 0
245 pos = 0
246 for rev in sortedstate:
246 for rev in sortedstate:
247 pos += 1
247 pos += 1
248 if state[rev] == -1:
248 if state[rev] == -1:
249 ui.progress(_("rebasing"), pos, ("%d:%s" % (rev, repo[rev])),
249 ui.progress(_("rebasing"), pos, ("%d:%s" % (rev, repo[rev])),
250 _('changesets'), total)
250 _('changesets'), total)
251 storestatus(repo, originalwd, target, state, collapsef, keepf,
251 storestatus(repo, originalwd, target, state, collapsef, keepf,
252 keepbranchesf, external)
252 keepbranchesf, external)
253 p1, p2 = defineparents(repo, rev, target, state,
253 p1, p2 = defineparents(repo, rev, target, state,
254 targetancestors)
254 targetancestors)
255 if len(repo.parents()) == 2:
255 if len(repo.parents()) == 2:
256 repo.ui.debug('resuming interrupted rebase\n')
256 repo.ui.debug('resuming interrupted rebase\n')
257 else:
257 else:
258 try:
258 try:
259 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''))
259 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''))
260 stats = rebasenode(repo, rev, p1, state, collapsef)
260 stats = rebasenode(repo, rev, p1, state, collapsef)
261 if stats and stats[3] > 0:
261 if stats and stats[3] > 0:
262 raise util.Abort(_('unresolved conflicts (see hg '
262 raise util.Abort(_('unresolved conflicts (see hg '
263 'resolve, then hg rebase --continue)'))
263 'resolve, then hg rebase --continue)'))
264 finally:
264 finally:
265 ui.setconfig('ui', 'forcemerge', '')
265 ui.setconfig('ui', 'forcemerge', '')
266 cmdutil.duplicatecopies(repo, rev, target)
266 cmdutil.duplicatecopies(repo, rev, target)
267 if not collapsef:
267 if not collapsef:
268 newrev = concludenode(repo, rev, p1, p2, extrafn=extrafn,
268 newrev = concludenode(repo, rev, p1, p2, extrafn=extrafn,
269 editor=editor)
269 editor=editor)
270 else:
270 else:
271 # Skip commit if we are collapsing
271 # Skip commit if we are collapsing
272 repo.setparents(repo[p1].node())
272 repo.setparents(repo[p1].node())
273 newrev = None
273 newrev = None
274 # Update the state
274 # Update the state
275 if newrev is not None:
275 if newrev is not None:
276 state[rev] = repo[newrev].rev()
276 state[rev] = repo[newrev].rev()
277 else:
277 else:
278 if not collapsef:
278 if not collapsef:
279 ui.note(_('no changes, revision %d skipped\n') % rev)
279 ui.note(_('no changes, revision %d skipped\n') % rev)
280 ui.debug('next revision set to %s\n' % p1)
280 ui.debug('next revision set to %s\n' % p1)
281 skipped.add(rev)
281 skipped.add(rev)
282 state[rev] = p1
282 state[rev] = p1
283
283
284 ui.progress(_('rebasing'), None)
284 ui.progress(_('rebasing'), None)
285 ui.note(_('rebase merging completed\n'))
285 ui.note(_('rebase merging completed\n'))
286
286
287 if collapsef and not keepopen:
287 if collapsef and not keepopen:
288 p1, p2 = defineparents(repo, min(state), target,
288 p1, p2 = defineparents(repo, min(state), target,
289 state, targetancestors)
289 state, targetancestors)
290 if collapsemsg:
290 if collapsemsg:
291 commitmsg = collapsemsg
291 commitmsg = collapsemsg
292 else:
292 else:
293 commitmsg = 'Collapsed revision'
293 commitmsg = 'Collapsed revision'
294 for rebased in state:
294 for rebased in state:
295 if rebased not in skipped and state[rebased] != nullmerge:
295 if rebased not in skipped and state[rebased] != nullmerge:
296 commitmsg += '\n* %s' % repo[rebased].description()
296 commitmsg += '\n* %s' % repo[rebased].description()
297 commitmsg = ui.edit(commitmsg, repo.ui.username())
297 commitmsg = ui.edit(commitmsg, repo.ui.username())
298 newrev = concludenode(repo, rev, p1, external, commitmsg=commitmsg,
298 newrev = concludenode(repo, rev, p1, external, commitmsg=commitmsg,
299 extrafn=extrafn, editor=editor)
299 extrafn=extrafn, editor=editor)
300
300
301 if 'qtip' in repo.tags():
301 if 'qtip' in repo.tags():
302 updatemq(repo, state, skipped, **opts)
302 updatemq(repo, state, skipped, **opts)
303
303
304 if currentbookmarks:
304 if currentbookmarks:
305 # Nodeids are needed to reset bookmarks
305 # Nodeids are needed to reset bookmarks
306 nstate = {}
306 nstate = {}
307 for k, v in state.iteritems():
307 for k, v in state.iteritems():
308 if v != nullmerge:
308 if v != nullmerge:
309 nstate[repo[k].node()] = repo[v].node()
309 nstate[repo[k].node()] = repo[v].node()
310
310
311 if not keepf:
311 if not keepf:
312 collapsedas = None
312 collapsedas = None
313 if collapsef:
313 if collapsef:
314 collapsedas = newrev
314 collapsedas = newrev
315 clearrebased(ui, repo, state, collapsedas)
315 clearrebased(ui, repo, state, collapsedas)
316
316
317 if currentbookmarks:
317 if currentbookmarks:
318 updatebookmarks(repo, nstate, currentbookmarks, **opts)
318 updatebookmarks(repo, nstate, currentbookmarks, **opts)
319
319
320 clearstatus(repo)
320 clearstatus(repo)
321 ui.note(_("rebase completed\n"))
321 ui.note(_("rebase completed\n"))
322 util.unlinkpath(repo.sjoin('undo'), ignoremissing=True)
322 util.unlinkpath(repo.sjoin('undo'), ignoremissing=True)
323 if skipped:
323 if skipped:
324 ui.note(_("%d revisions have been skipped\n") % len(skipped))
324 ui.note(_("%d revisions have been skipped\n") % len(skipped))
325
325
326 if (activebookmark and
326 if (activebookmark and
327 repo['tip'].node() == repo._bookmarks[activebookmark]):
327 repo['tip'].node() == repo._bookmarks[activebookmark]):
328 bookmarks.setcurrent(repo, activebookmark)
328 bookmarks.setcurrent(repo, activebookmark)
329
329
330 finally:
330 finally:
331 release(lock, wlock)
331 release(lock, wlock)
332
332
333 def checkexternal(repo, state, targetancestors):
333 def checkexternal(repo, state, targetancestors):
334 """Check whether one or more external revisions need to be taken in
334 """Check whether one or more external revisions need to be taken in
335 consideration. In the latter case, abort.
335 consideration. In the latter case, abort.
336 """
336 """
337 external = nullrev
337 external = nullrev
338 source = min(state)
338 source = min(state)
339 for rev in state:
339 for rev in state:
340 if rev == source:
340 if rev == source:
341 continue
341 continue
342 # Check externals and fail if there are more than one
342 # Check externals and fail if there are more than one
343 for p in repo[rev].parents():
343 for p in repo[rev].parents():
344 if (p.rev() not in state
344 if (p.rev() not in state
345 and p.rev() not in targetancestors):
345 and p.rev() not in targetancestors):
346 if external != nullrev:
346 if external != nullrev:
347 raise util.Abort(_('unable to collapse, there is more '
347 raise util.Abort(_('unable to collapse, there is more '
348 'than one external parent'))
348 'than one external parent'))
349 external = p.rev()
349 external = p.rev()
350 return external
350 return external
351
351
352 def concludenode(repo, rev, p1, p2, commitmsg=None, editor=None, extrafn=None):
352 def concludenode(repo, rev, p1, p2, commitmsg=None, editor=None, extrafn=None):
353 'Commit the changes and store useful information in extra'
353 'Commit the changes and store useful information in extra'
354 try:
354 try:
355 repo.setparents(repo[p1].node(), repo[p2].node())
355 repo.setparents(repo[p1].node(), repo[p2].node())
356 ctx = repo[rev]
356 ctx = repo[rev]
357 if commitmsg is None:
357 if commitmsg is None:
358 commitmsg = ctx.description()
358 commitmsg = ctx.description()
359 extra = {'rebase_source': ctx.hex()}
359 extra = {'rebase_source': ctx.hex()}
360 if extrafn:
360 if extrafn:
361 extrafn(ctx, extra)
361 extrafn(ctx, extra)
362 # Commit might fail if unresolved files exist
362 # Commit might fail if unresolved files exist
363 newrev = repo.commit(text=commitmsg, user=ctx.user(),
363 newrev = repo.commit(text=commitmsg, user=ctx.user(),
364 date=ctx.date(), extra=extra, editor=editor)
364 date=ctx.date(), extra=extra, editor=editor)
365 repo.dirstate.setbranch(repo[newrev].branch())
365 repo.dirstate.setbranch(repo[newrev].branch())
366 targetphase = max(ctx.phase(), phases.draft)
366 targetphase = max(ctx.phase(), phases.draft)
367 # retractboundary doesn't overwrite upper phase inherited from parent
367 # retractboundary doesn't overwrite upper phase inherited from parent
368 newnode = repo[newrev].node()
368 newnode = repo[newrev].node()
369 if newnode:
369 if newnode:
370 phases.retractboundary(repo, targetphase, [newnode])
370 phases.retractboundary(repo, targetphase, [newnode])
371 return newrev
371 return newrev
372 except util.Abort:
372 except util.Abort:
373 # Invalidate the previous setparents
373 # Invalidate the previous setparents
374 repo.dirstate.invalidate()
374 repo.dirstate.invalidate()
375 raise
375 raise
376
376
377 def rebasenode(repo, rev, p1, state, collapse):
377 def rebasenode(repo, rev, p1, state, collapse):
378 'Rebase a single revision'
378 'Rebase a single revision'
379 # Merge phase
379 # Merge phase
380 # Update to target and merge it with local
380 # Update to target and merge it with local
381 if repo['.'].rev() != repo[p1].rev():
381 if repo['.'].rev() != repo[p1].rev():
382 repo.ui.debug(" update to %d:%s\n" % (repo[p1].rev(), repo[p1]))
382 repo.ui.debug(" update to %d:%s\n" % (repo[p1].rev(), repo[p1]))
383 merge.update(repo, p1, False, True, False)
383 merge.update(repo, p1, False, True, False)
384 else:
384 else:
385 repo.ui.debug(" already in target\n")
385 repo.ui.debug(" already in target\n")
386 repo.dirstate.write()
386 repo.dirstate.write()
387 repo.ui.debug(" merge against %d:%s\n" % (repo[rev].rev(), repo[rev]))
387 repo.ui.debug(" merge against %d:%s\n" % (repo[rev].rev(), repo[rev]))
388 base = None
388 base = None
389 if repo[rev].rev() != repo[min(state)].rev():
389 if repo[rev].rev() != repo[min(state)].rev():
390 base = repo[rev].p1().node()
390 base = repo[rev].p1().node()
391 # When collapsing in-place, the parent is the common ancestor, we
391 # When collapsing in-place, the parent is the common ancestor, we
392 # have to allow merging with it.
392 # have to allow merging with it.
393 return merge.update(repo, rev, True, True, False, base, collapse)
393 return merge.update(repo, rev, True, True, False, base, collapse)
394
394
395 def defineparents(repo, rev, target, state, targetancestors):
395 def defineparents(repo, rev, target, state, targetancestors):
396 'Return the new parent relationship of the revision that will be rebased'
396 'Return the new parent relationship of the revision that will be rebased'
397 parents = repo[rev].parents()
397 parents = repo[rev].parents()
398 p1 = p2 = nullrev
398 p1 = p2 = nullrev
399
399
400 P1n = parents[0].rev()
400 P1n = parents[0].rev()
401 if P1n in targetancestors:
401 if P1n in targetancestors:
402 p1 = target
402 p1 = target
403 elif P1n in state:
403 elif P1n in state:
404 if state[P1n] == nullmerge:
404 if state[P1n] == nullmerge:
405 p1 = target
405 p1 = target
406 else:
406 else:
407 p1 = state[P1n]
407 p1 = state[P1n]
408 else: # P1n external
408 else: # P1n external
409 p1 = target
409 p1 = target
410 p2 = P1n
410 p2 = P1n
411
411
412 if len(parents) == 2 and parents[1].rev() not in targetancestors:
412 if len(parents) == 2 and parents[1].rev() not in targetancestors:
413 P2n = parents[1].rev()
413 P2n = parents[1].rev()
414 # interesting second parent
414 # interesting second parent
415 if P2n in state:
415 if P2n in state:
416 if p1 == target: # P1n in targetancestors or external
416 if p1 == target: # P1n in targetancestors or external
417 p1 = state[P2n]
417 p1 = state[P2n]
418 else:
418 else:
419 p2 = state[P2n]
419 p2 = state[P2n]
420 else: # P2n external
420 else: # P2n external
421 if p2 != nullrev: # P1n external too => rev is a merged revision
421 if p2 != nullrev: # P1n external too => rev is a merged revision
422 raise util.Abort(_('cannot use revision %d as base, result '
422 raise util.Abort(_('cannot use revision %d as base, result '
423 'would have 3 parents') % rev)
423 'would have 3 parents') % rev)
424 p2 = P2n
424 p2 = P2n
425 repo.ui.debug(" future parents are %d and %d\n" %
425 repo.ui.debug(" future parents are %d and %d\n" %
426 (repo[p1].rev(), repo[p2].rev()))
426 (repo[p1].rev(), repo[p2].rev()))
427 return p1, p2
427 return p1, p2
428
428
429 def isagitpatch(repo, patchname):
429 def isagitpatch(repo, patchname):
430 'Return true if the given patch is in git format'
430 'Return true if the given patch is in git format'
431 mqpatch = os.path.join(repo.mq.path, patchname)
431 mqpatch = os.path.join(repo.mq.path, patchname)
432 for line in patch.linereader(file(mqpatch, 'rb')):
432 for line in patch.linereader(file(mqpatch, 'rb')):
433 if line.startswith('diff --git'):
433 if line.startswith('diff --git'):
434 return True
434 return True
435 return False
435 return False
436
436
437 def updatemq(repo, state, skipped, **opts):
437 def updatemq(repo, state, skipped, **opts):
438 'Update rebased mq patches - finalize and then import them'
438 'Update rebased mq patches - finalize and then import them'
439 mqrebase = {}
439 mqrebase = {}
440 mq = repo.mq
440 mq = repo.mq
441 original_series = mq.fullseries[:]
441 original_series = mq.fullseries[:]
442 skippedpatches = set()
442 skippedpatches = set()
443
443
444 for p in mq.applied:
444 for p in mq.applied:
445 rev = repo[p.node].rev()
445 rev = repo[p.node].rev()
446 if rev in state:
446 if rev in state:
447 repo.ui.debug('revision %d is an mq patch (%s), finalize it.\n' %
447 repo.ui.debug('revision %d is an mq patch (%s), finalize it.\n' %
448 (rev, p.name))
448 (rev, p.name))
449 mqrebase[rev] = (p.name, isagitpatch(repo, p.name))
449 mqrebase[rev] = (p.name, isagitpatch(repo, p.name))
450 else:
450 else:
451 # Applied but not rebased, not sure this should happen
451 # Applied but not rebased, not sure this should happen
452 skippedpatches.add(p.name)
452 skippedpatches.add(p.name)
453
453
454 if mqrebase:
454 if mqrebase:
455 mq.finish(repo, mqrebase.keys())
455 mq.finish(repo, mqrebase.keys())
456
456
457 # We must start import from the newest revision
457 # We must start import from the newest revision
458 for rev in sorted(mqrebase, reverse=True):
458 for rev in sorted(mqrebase, reverse=True):
459 if rev not in skipped:
459 if rev not in skipped:
460 name, isgit = mqrebase[rev]
460 name, isgit = mqrebase[rev]
461 repo.ui.debug('import mq patch %d (%s)\n' % (state[rev], name))
461 repo.ui.debug('import mq patch %d (%s)\n' % (state[rev], name))
462 mq.qimport(repo, (), patchname=name, git=isgit,
462 mq.qimport(repo, (), patchname=name, git=isgit,
463 rev=[str(state[rev])])
463 rev=[str(state[rev])])
464 else:
464 else:
465 # Rebased and skipped
465 # Rebased and skipped
466 skippedpatches.add(mqrebase[rev][0])
466 skippedpatches.add(mqrebase[rev][0])
467
467
468 # Patches were either applied and rebased and imported in
468 # Patches were either applied and rebased and imported in
469 # order, applied and removed or unapplied. Discard the removed
469 # order, applied and removed or unapplied. Discard the removed
470 # ones while preserving the original series order and guards.
470 # ones while preserving the original series order and guards.
471 newseries = [s for s in original_series
471 newseries = [s for s in original_series
472 if mq.guard_re.split(s, 1)[0] not in skippedpatches]
472 if mq.guard_re.split(s, 1)[0] not in skippedpatches]
473 mq.fullseries[:] = newseries
473 mq.fullseries[:] = newseries
474 mq.seriesdirty = True
474 mq.seriesdirty = True
475 mq.savedirty()
475 mq.savedirty()
476
476
477 def updatebookmarks(repo, nstate, originalbookmarks, **opts):
477 def updatebookmarks(repo, nstate, originalbookmarks, **opts):
478 'Move bookmarks to their correct changesets'
478 'Move bookmarks to their correct changesets'
479 marks = repo._bookmarks
479 marks = repo._bookmarks
480 for k, v in originalbookmarks.iteritems():
480 for k, v in originalbookmarks.iteritems():
481 if v in nstate:
481 if v in nstate:
482 if nstate[v] != nullmerge:
482 if nstate[v] != nullmerge:
483 # update the bookmarks for revs that have moved
483 # update the bookmarks for revs that have moved
484 marks[k] = nstate[v]
484 marks[k] = nstate[v]
485
485
486 marks.write()
486 marks.write()
487
487
488 def storestatus(repo, originalwd, target, state, collapse, keep, keepbranches,
488 def storestatus(repo, originalwd, target, state, collapse, keep, keepbranches,
489 external):
489 external):
490 'Store the current status to allow recovery'
490 'Store the current status to allow recovery'
491 f = repo.opener("rebasestate", "w")
491 f = repo.opener("rebasestate", "w")
492 f.write(repo[originalwd].hex() + '\n')
492 f.write(repo[originalwd].hex() + '\n')
493 f.write(repo[target].hex() + '\n')
493 f.write(repo[target].hex() + '\n')
494 f.write(repo[external].hex() + '\n')
494 f.write(repo[external].hex() + '\n')
495 f.write('%d\n' % int(collapse))
495 f.write('%d\n' % int(collapse))
496 f.write('%d\n' % int(keep))
496 f.write('%d\n' % int(keep))
497 f.write('%d\n' % int(keepbranches))
497 f.write('%d\n' % int(keepbranches))
498 for d, v in state.iteritems():
498 for d, v in state.iteritems():
499 oldrev = repo[d].hex()
499 oldrev = repo[d].hex()
500 if v != nullmerge:
500 if v != nullmerge:
501 newrev = repo[v].hex()
501 newrev = repo[v].hex()
502 else:
502 else:
503 newrev = v
503 newrev = v
504 f.write("%s:%s\n" % (oldrev, newrev))
504 f.write("%s:%s\n" % (oldrev, newrev))
505 f.close()
505 f.close()
506 repo.ui.debug('rebase status stored\n')
506 repo.ui.debug('rebase status stored\n')
507
507
508 def clearstatus(repo):
508 def clearstatus(repo):
509 'Remove the status files'
509 'Remove the status files'
510 util.unlinkpath(repo.join("rebasestate"), ignoremissing=True)
510 util.unlinkpath(repo.join("rebasestate"), ignoremissing=True)
511
511
512 def restorestatus(repo):
512 def restorestatus(repo):
513 'Restore a previously stored status'
513 'Restore a previously stored status'
514 try:
514 try:
515 target = None
515 target = None
516 collapse = False
516 collapse = False
517 external = nullrev
517 external = nullrev
518 state = {}
518 state = {}
519 f = repo.opener("rebasestate")
519 f = repo.opener("rebasestate")
520 for i, l in enumerate(f.read().splitlines()):
520 for i, l in enumerate(f.read().splitlines()):
521 if i == 0:
521 if i == 0:
522 originalwd = repo[l].rev()
522 originalwd = repo[l].rev()
523 elif i == 1:
523 elif i == 1:
524 target = repo[l].rev()
524 target = repo[l].rev()
525 elif i == 2:
525 elif i == 2:
526 external = repo[l].rev()
526 external = repo[l].rev()
527 elif i == 3:
527 elif i == 3:
528 collapse = bool(int(l))
528 collapse = bool(int(l))
529 elif i == 4:
529 elif i == 4:
530 keep = bool(int(l))
530 keep = bool(int(l))
531 elif i == 5:
531 elif i == 5:
532 keepbranches = bool(int(l))
532 keepbranches = bool(int(l))
533 else:
533 else:
534 oldrev, newrev = l.split(':')
534 oldrev, newrev = l.split(':')
535 if newrev != str(nullmerge):
535 if newrev != str(nullmerge):
536 state[repo[oldrev].rev()] = repo[newrev].rev()
536 state[repo[oldrev].rev()] = repo[newrev].rev()
537 else:
537 else:
538 state[repo[oldrev].rev()] = int(newrev)
538 state[repo[oldrev].rev()] = int(newrev)
539 skipped = set()
539 skipped = set()
540 # recompute the set of skipped revs
540 # recompute the set of skipped revs
541 if not collapse:
541 if not collapse:
542 seen = set([target])
542 seen = set([target])
543 for old, new in sorted(state.items()):
543 for old, new in sorted(state.items()):
544 if new != nullrev and new in seen:
544 if new != nullrev and new in seen:
545 skipped.add(old)
545 skipped.add(old)
546 seen.add(new)
546 seen.add(new)
547 repo.ui.debug('computed skipped revs: %s\n' % skipped)
547 repo.ui.debug('computed skipped revs: %s\n' % skipped)
548 repo.ui.debug('rebase status resumed\n')
548 repo.ui.debug('rebase status resumed\n')
549 return (originalwd, target, state, skipped,
549 return (originalwd, target, state, skipped,
550 collapse, keep, keepbranches, external)
550 collapse, keep, keepbranches, external)
551 except IOError, err:
551 except IOError, err:
552 if err.errno != errno.ENOENT:
552 if err.errno != errno.ENOENT:
553 raise
553 raise
554 raise util.Abort(_('no rebase in progress'))
554 raise util.Abort(_('no rebase in progress'))
555
555
556 def abort(repo, originalwd, target, state):
556 def abort(repo, originalwd, target, state):
557 'Restore the repository to its original state'
557 'Restore the repository to its original state'
558 dstates = [s for s in state.values() if s != nullrev]
558 dstates = [s for s in state.values() if s != nullrev]
559 immutable = [d for d in dstates if not repo[d].mutable()]
559 immutable = [d for d in dstates if not repo[d].mutable()]
560 if immutable:
560 if immutable:
561 raise util.Abort(_("can't abort rebase due to immutable changesets %s")
561 raise util.Abort(_("can't abort rebase due to immutable changesets %s")
562 % ', '.join(str(repo[r]) for r in immutable),
562 % ', '.join(str(repo[r]) for r in immutable),
563 hint=_('see hg help phases for details'))
563 hint=_('see hg help phases for details'))
564
564
565 descendants = set()
565 descendants = set()
566 if dstates:
566 if dstates:
567 descendants = set(repo.changelog.descendants(dstates))
567 descendants = set(repo.changelog.descendants(dstates))
568 if descendants - set(dstates):
568 if descendants - set(dstates):
569 repo.ui.warn(_("warning: new changesets detected on target branch, "
569 repo.ui.warn(_("warning: new changesets detected on target branch, "
570 "can't abort\n"))
570 "can't abort\n"))
571 return -1
571 return -1
572 else:
572 else:
573 # Strip from the first rebased revision
573 # Strip from the first rebased revision
574 merge.update(repo, repo[originalwd].rev(), False, True, False)
574 merge.update(repo, repo[originalwd].rev(), False, True, False)
575 rebased = filter(lambda x: x > -1 and x != target, state.values())
575 rebased = filter(lambda x: x > -1 and x != target, state.values())
576 if rebased:
576 if rebased:
577 strippoint = min(rebased)
577 strippoints = [c.node() for c in repo.set('roots(%ld)', rebased)]
578 # no backup of rebased cset versions needed
578 # no backup of rebased cset versions needed
579 repair.strip(repo.ui, repo, repo[strippoint].node())
579 repair.strip(repo.ui, repo, strippoints)
580 clearstatus(repo)
580 clearstatus(repo)
581 repo.ui.warn(_('rebase aborted\n'))
581 repo.ui.warn(_('rebase aborted\n'))
582 return 0
582 return 0
583
583
584 def buildstate(repo, dest, rebaseset, collapse):
584 def buildstate(repo, dest, rebaseset, collapse):
585 '''Define which revisions are going to be rebased and where
585 '''Define which revisions are going to be rebased and where
586
586
587 repo: repo
587 repo: repo
588 dest: context
588 dest: context
589 rebaseset: set of rev
589 rebaseset: set of rev
590 '''
590 '''
591
591
592 # This check isn't strictly necessary, since mq detects commits over an
592 # This check isn't strictly necessary, since mq detects commits over an
593 # applied patch. But it prevents messing up the working directory when
593 # applied patch. But it prevents messing up the working directory when
594 # a partially completed rebase is blocked by mq.
594 # a partially completed rebase is blocked by mq.
595 if 'qtip' in repo.tags() and (dest.node() in
595 if 'qtip' in repo.tags() and (dest.node() in
596 [s.node for s in repo.mq.applied]):
596 [s.node for s in repo.mq.applied]):
597 raise util.Abort(_('cannot rebase onto an applied mq patch'))
597 raise util.Abort(_('cannot rebase onto an applied mq patch'))
598
598
599 roots = list(repo.set('roots(%ld)', rebaseset))
599 roots = list(repo.set('roots(%ld)', rebaseset))
600 if not roots:
600 if not roots:
601 raise util.Abort(_('no matching revisions'))
601 raise util.Abort(_('no matching revisions'))
602 if len(roots) > 1:
602 roots.sort()
603 raise util.Abort(_("can't rebase multiple roots"))
603 state = {}
604 root = roots[0]
604 detachset = set()
605
605 for root in roots:
606 commonbase = root.ancestor(dest)
606 commonbase = root.ancestor(dest)
607 if commonbase == root:
607 if commonbase == root:
608 raise util.Abort(_('source is ancestor of destination'))
608 raise util.Abort(_('source is ancestor of destination'))
609 if commonbase == dest:
609 if commonbase == dest:
610 samebranch = root.branch() == dest.branch()
610 samebranch = root.branch() == dest.branch()
611 if not collapse and samebranch and root in dest.children():
611 if not collapse and samebranch and root in dest.children():
612 repo.ui.debug('source is a child of destination\n')
612 repo.ui.debug('source is a child of destination\n')
613 return None
613 return None
614
614
615 repo.ui.debug('rebase onto %d starting from %d\n' % (dest, root))
615 repo.ui.debug('rebase onto %d starting from %s\n' % (dest, roots))
616 state = dict.fromkeys(rebaseset, nullrev)
616 state.update(dict.fromkeys(rebaseset, nullrev))
617 # Rebase tries to turn <dest> into a parent of <root> while
617 # Rebase tries to turn <dest> into a parent of <root> while
618 # preserving the number of parents of rebased changesets:
618 # preserving the number of parents of rebased changesets:
619 #
619 #
620 # - A changeset with a single parent will always be rebased as a
620 # - A changeset with a single parent will always be rebased as a
621 # changeset with a single parent.
621 # changeset with a single parent.
622 #
622 #
623 # - A merge will be rebased as merge unless its parents are both
623 # - A merge will be rebased as merge unless its parents are both
624 # ancestors of <dest> or are themselves in the rebased set and
624 # ancestors of <dest> or are themselves in the rebased set and
625 # pruned while rebased.
625 # pruned while rebased.
626 #
626 #
627 # If one parent of <root> is an ancestor of <dest>, the rebased
627 # If one parent of <root> is an ancestor of <dest>, the rebased
628 # version of this parent will be <dest>. This is always true with
628 # version of this parent will be <dest>. This is always true with
629 # --base option.
629 # --base option.
630 #
630 #
631 # Otherwise, we need to *replace* the original parents with
631 # Otherwise, we need to *replace* the original parents with
632 # <dest>. This "detaches" the rebased set from its former location
632 # <dest>. This "detaches" the rebased set from its former location
633 # and rebases it onto <dest>. Changes introduced by ancestors of
633 # and rebases it onto <dest>. Changes introduced by ancestors of
634 # <root> not common with <dest> (the detachset, marked as
634 # <root> not common with <dest> (the detachset, marked as
635 # nullmerge) are "removed" from the rebased changesets.
635 # nullmerge) are "removed" from the rebased changesets.
636 #
636 #
637 # - If <root> has a single parent, set it to <dest>.
637 # - If <root> has a single parent, set it to <dest>.
638 #
638 #
639 # - If <root> is a merge, we cannot decide which parent to
639 # - If <root> is a merge, we cannot decide which parent to
640 # replace, the rebase operation is not clearly defined.
640 # replace, the rebase operation is not clearly defined.
641 #
641 #
642 # The table below sums up this behavior:
642 # The table below sums up this behavior:
643 #
643 #
644 # +--------------------+----------------------+-------------------------+
644 # +------------------+----------------------+-------------------------+
645 # | | one parent | merge |
645 # | | one parent | merge |
646 # +--------------------+----------------------+-------------------------+
646 # +------------------+----------------------+-------------------------+
647 # | parent in ::<dest> | new parent is <dest> | parents in ::<dest> are |
647 # | parent in | new parent is <dest> | parents in ::<dest> are |
648 # | | | remapped to <dest> |
648 # | ::<dest> | | remapped to <dest> |
649 # +--------------------+----------------------+-------------------------+
649 # +------------------+----------------------+-------------------------+
650 # | unrelated source | new parent is <dest> | ambiguous, abort |
650 # | unrelated source | new parent is <dest> | ambiguous, abort |
651 # +--------------------+----------------------+-------------------------+
651 # +------------------+----------------------+-------------------------+
652 #
652 #
653 # The actual abort is handled by `defineparents`
653 # The actual abort is handled by `defineparents`
654 if len(root.parents()) <= 1:
654 if len(root.parents()) <= 1:
655 # ancestors of <root> not ancestors of <dest>
655 # ancestors of <root> not ancestors of <dest>
656 detachset = repo.changelog.findmissingrevs([commonbase.rev()],
656 detachset.update(repo.changelog.findmissingrevs([commonbase.rev()],
657 [root.rev()])
657 [root.rev()]))
658 state.update(dict.fromkeys(detachset, nullmerge))
658 for r in detachset:
659 # detachset can have root, and we definitely want to rebase that
659 if r not in state:
660 state[root.rev()] = nullrev
660 state[r] = nullmerge
661 return repo['.'].rev(), dest.rev(), state
661 return repo['.'].rev(), dest.rev(), state
662
662
663 def clearrebased(ui, repo, state, collapsedas=None):
663 def clearrebased(ui, repo, state, collapsedas=None):
664 """dispose of rebased revision at the end of the rebase
664 """dispose of rebased revision at the end of the rebase
665
665
666 If `collapsedas` is not None, the rebase was a collapse whose result if the
666 If `collapsedas` is not None, the rebase was a collapse whose result if the
667 `collapsedas` node."""
667 `collapsedas` node."""
668 if obsolete._enabled:
668 if obsolete._enabled:
669 markers = []
669 markers = []
670 for rev, newrev in sorted(state.items()):
670 for rev, newrev in sorted(state.items()):
671 if newrev >= 0:
671 if newrev >= 0:
672 if collapsedas is not None:
672 if collapsedas is not None:
673 newrev = collapsedas
673 newrev = collapsedas
674 markers.append((repo[rev], (repo[newrev],)))
674 markers.append((repo[rev], (repo[newrev],)))
675 if markers:
675 if markers:
676 obsolete.createmarkers(repo, markers)
676 obsolete.createmarkers(repo, markers)
677 else:
677 else:
678 rebased = [rev for rev in state if state[rev] != nullmerge]
678 rebased = [rev for rev in state if state[rev] != nullmerge]
679 if rebased:
679 if rebased:
680 if set(repo.changelog.descendants([min(rebased)])) - set(state):
680 stripped = []
681 for root in repo.set('roots(%ld)', rebased):
682 if set(repo.changelog.descendants([root.rev()])) - set(state):
681 ui.warn(_("warning: new changesets detected "
683 ui.warn(_("warning: new changesets detected "
682 "on source branch, not stripping\n"))
684 "on source branch, not stripping\n"))
683 else:
685 else:
686 stripped.append(root.node())
687 if stripped:
684 # backup the old csets by default
688 # backup the old csets by default
685 repair.strip(ui, repo, repo[min(rebased)].node(), "all")
689 repair.strip(ui, repo, stripped, "all")
686
690
687
691
688 def pullrebase(orig, ui, repo, *args, **opts):
692 def pullrebase(orig, ui, repo, *args, **opts):
689 'Call rebase after pull if the latter has been invoked with --rebase'
693 'Call rebase after pull if the latter has been invoked with --rebase'
690 if opts.get('rebase'):
694 if opts.get('rebase'):
691 if opts.get('update'):
695 if opts.get('update'):
692 del opts['update']
696 del opts['update']
693 ui.debug('--update and --rebase are not compatible, ignoring '
697 ui.debug('--update and --rebase are not compatible, ignoring '
694 'the update flag\n')
698 'the update flag\n')
695
699
696 movemarkfrom = repo['.'].node()
700 movemarkfrom = repo['.'].node()
697 cmdutil.bailifchanged(repo)
701 cmdutil.bailifchanged(repo)
698 revsprepull = len(repo)
702 revsprepull = len(repo)
699 origpostincoming = commands.postincoming
703 origpostincoming = commands.postincoming
700 def _dummy(*args, **kwargs):
704 def _dummy(*args, **kwargs):
701 pass
705 pass
702 commands.postincoming = _dummy
706 commands.postincoming = _dummy
703 try:
707 try:
704 orig(ui, repo, *args, **opts)
708 orig(ui, repo, *args, **opts)
705 finally:
709 finally:
706 commands.postincoming = origpostincoming
710 commands.postincoming = origpostincoming
707 revspostpull = len(repo)
711 revspostpull = len(repo)
708 if revspostpull > revsprepull:
712 if revspostpull > revsprepull:
709 # --rev option from pull conflict with rebase own --rev
713 # --rev option from pull conflict with rebase own --rev
710 # dropping it
714 # dropping it
711 if 'rev' in opts:
715 if 'rev' in opts:
712 del opts['rev']
716 del opts['rev']
713 rebase(ui, repo, **opts)
717 rebase(ui, repo, **opts)
714 branch = repo[None].branch()
718 branch = repo[None].branch()
715 dest = repo[branch].rev()
719 dest = repo[branch].rev()
716 if dest != repo['.'].rev():
720 if dest != repo['.'].rev():
717 # there was nothing to rebase we force an update
721 # there was nothing to rebase we force an update
718 hg.update(repo, dest)
722 hg.update(repo, dest)
719 if bookmarks.update(repo, [movemarkfrom], repo['.'].node()):
723 if bookmarks.update(repo, [movemarkfrom], repo['.'].node()):
720 ui.status(_("updating bookmark %s\n")
724 ui.status(_("updating bookmark %s\n")
721 % repo._bookmarkcurrent)
725 % repo._bookmarkcurrent)
722 else:
726 else:
723 if opts.get('tool'):
727 if opts.get('tool'):
724 raise util.Abort(_('--tool can only be used with --rebase'))
728 raise util.Abort(_('--tool can only be used with --rebase'))
725 orig(ui, repo, *args, **opts)
729 orig(ui, repo, *args, **opts)
726
730
727 def uisetup(ui):
731 def uisetup(ui):
728 'Replace pull with a decorator to provide --rebase option'
732 'Replace pull with a decorator to provide --rebase option'
729 entry = extensions.wrapcommand(commands.table, 'pull', pullrebase)
733 entry = extensions.wrapcommand(commands.table, 'pull', pullrebase)
730 entry[1].append(('', 'rebase', None,
734 entry[1].append(('', 'rebase', None,
731 _("rebase working directory to branch head")))
735 _("rebase working directory to branch head")))
732 entry[1].append(('t', 'tool', '',
736 entry[1].append(('t', 'tool', '',
733 _("specify merge tool for rebase")))
737 _("specify merge tool for rebase")))
@@ -1,308 +1,331 b''
1 ==========================
1 ==========================
2 Test rebase with obsolete
2 Test rebase with obsolete
3 ==========================
3 ==========================
4
4
5 Enable obsolete
5 Enable obsolete
6
6
7 $ cat > ${TESTTMP}/obs.py << EOF
7 $ cat > ${TESTTMP}/obs.py << EOF
8 > import mercurial.obsolete
8 > import mercurial.obsolete
9 > mercurial.obsolete._enabled = True
9 > mercurial.obsolete._enabled = True
10 > EOF
10 > EOF
11 $ cat >> $HGRCPATH << EOF
11 $ cat >> $HGRCPATH << EOF
12 > [ui]
12 > [ui]
13 > logtemplate= {rev}:{node|short} {desc|firstline}
13 > logtemplate= {rev}:{node|short} {desc|firstline}
14 > [phases]
14 > [phases]
15 > publish=False
15 > publish=False
16 > [extensions]'
16 > [extensions]'
17 > rebase=
17 > rebase=
18 >
18 >
19 > obs=${TESTTMP}/obs.py
19 > obs=${TESTTMP}/obs.py
20 > EOF
20 > EOF
21
21
22 Setup rebase canonical repo
22 Setup rebase canonical repo
23
23
24 $ hg init base
24 $ hg init base
25 $ cd base
25 $ cd base
26 $ hg unbundle "$TESTDIR/bundles/rebase.hg"
26 $ hg unbundle "$TESTDIR/bundles/rebase.hg"
27 adding changesets
27 adding changesets
28 adding manifests
28 adding manifests
29 adding file changes
29 adding file changes
30 added 8 changesets with 7 changes to 7 files (+2 heads)
30 added 8 changesets with 7 changes to 7 files (+2 heads)
31 (run 'hg heads' to see heads, 'hg merge' to merge)
31 (run 'hg heads' to see heads, 'hg merge' to merge)
32 $ hg up tip
32 $ hg up tip
33 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
33 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
34 $ hg log -G
34 $ hg log -G
35 @ 7:02de42196ebe H
35 @ 7:02de42196ebe H
36 |
36 |
37 | o 6:eea13746799a G
37 | o 6:eea13746799a G
38 |/|
38 |/|
39 o | 5:24b6387c8c8c F
39 o | 5:24b6387c8c8c F
40 | |
40 | |
41 | o 4:9520eea781bc E
41 | o 4:9520eea781bc E
42 |/
42 |/
43 | o 3:32af7686d403 D
43 | o 3:32af7686d403 D
44 | |
44 | |
45 | o 2:5fddd98957c8 C
45 | o 2:5fddd98957c8 C
46 | |
46 | |
47 | o 1:42ccdea3bb16 B
47 | o 1:42ccdea3bb16 B
48 |/
48 |/
49 o 0:cd010b8cd998 A
49 o 0:cd010b8cd998 A
50
50
51 $ cd ..
51 $ cd ..
52
52
53 simple rebase
53 simple rebase
54 ---------------------------------
54 ---------------------------------
55
55
56 $ hg clone base simple
56 $ hg clone base simple
57 updating to branch default
57 updating to branch default
58 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
58 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
59 $ cd simple
59 $ cd simple
60 $ hg up 32af7686d403
60 $ hg up 32af7686d403
61 3 files updated, 0 files merged, 2 files removed, 0 files unresolved
61 3 files updated, 0 files merged, 2 files removed, 0 files unresolved
62 $ hg rebase -d eea13746799a
62 $ hg rebase -d eea13746799a
63 $ hg log -G
63 $ hg log -G
64 @ 10:8eeb3c33ad33 D
64 @ 10:8eeb3c33ad33 D
65 |
65 |
66 o 9:2327fea05063 C
66 o 9:2327fea05063 C
67 |
67 |
68 o 8:e4e5be0395b2 B
68 o 8:e4e5be0395b2 B
69 |
69 |
70 | o 7:02de42196ebe H
70 | o 7:02de42196ebe H
71 | |
71 | |
72 o | 6:eea13746799a G
72 o | 6:eea13746799a G
73 |\|
73 |\|
74 | o 5:24b6387c8c8c F
74 | o 5:24b6387c8c8c F
75 | |
75 | |
76 o | 4:9520eea781bc E
76 o | 4:9520eea781bc E
77 |/
77 |/
78 o 0:cd010b8cd998 A
78 o 0:cd010b8cd998 A
79
79
80 $ hg log --hidden -G
80 $ hg log --hidden -G
81 @ 10:8eeb3c33ad33 D
81 @ 10:8eeb3c33ad33 D
82 |
82 |
83 o 9:2327fea05063 C
83 o 9:2327fea05063 C
84 |
84 |
85 o 8:e4e5be0395b2 B
85 o 8:e4e5be0395b2 B
86 |
86 |
87 | o 7:02de42196ebe H
87 | o 7:02de42196ebe H
88 | |
88 | |
89 o | 6:eea13746799a G
89 o | 6:eea13746799a G
90 |\|
90 |\|
91 | o 5:24b6387c8c8c F
91 | o 5:24b6387c8c8c F
92 | |
92 | |
93 o | 4:9520eea781bc E
93 o | 4:9520eea781bc E
94 |/
94 |/
95 | x 3:32af7686d403 D
95 | x 3:32af7686d403 D
96 | |
96 | |
97 | x 2:5fddd98957c8 C
97 | x 2:5fddd98957c8 C
98 | |
98 | |
99 | x 1:42ccdea3bb16 B
99 | x 1:42ccdea3bb16 B
100 |/
100 |/
101 o 0:cd010b8cd998 A
101 o 0:cd010b8cd998 A
102
102
103 $ hg debugobsolete
103 $ hg debugobsolete
104 42ccdea3bb16d28e1848c95fe2e44c000f3f21b1 e4e5be0395b2cbd471ed22a26b1b6a1a0658a794 0 {'date': '*', 'user': 'test'} (glob)
104 42ccdea3bb16d28e1848c95fe2e44c000f3f21b1 e4e5be0395b2cbd471ed22a26b1b6a1a0658a794 0 {'date': '*', 'user': 'test'} (glob)
105 5fddd98957c8a54a4d436dfe1da9d87f21a1b97b 2327fea05063f39961b14cb69435a9898dc9a245 0 {'date': '*', 'user': 'test'} (glob)
105 5fddd98957c8a54a4d436dfe1da9d87f21a1b97b 2327fea05063f39961b14cb69435a9898dc9a245 0 {'date': '*', 'user': 'test'} (glob)
106 32af7686d403cf45b5d95f2d70cebea587ac806a 8eeb3c33ad33d452c89e5dcf611c347f978fb42b 0 {'date': '*', 'user': 'test'} (glob)
106 32af7686d403cf45b5d95f2d70cebea587ac806a 8eeb3c33ad33d452c89e5dcf611c347f978fb42b 0 {'date': '*', 'user': 'test'} (glob)
107
107
108
108
109 $ cd ..
109 $ cd ..
110
110
111 empty changeset
111 empty changeset
112 ---------------------------------
112 ---------------------------------
113
113
114 $ hg clone base empty
114 $ hg clone base empty
115 updating to branch default
115 updating to branch default
116 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
116 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
117 $ cd empty
117 $ cd empty
118 $ hg up eea13746799a
118 $ hg up eea13746799a
119 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
119 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
120
120
121 We make a copy of both the first changeset in the rebased and some other in the
121 We make a copy of both the first changeset in the rebased and some other in the
122 set.
122 set.
123
123
124 $ hg graft 42ccdea3bb16 32af7686d403
124 $ hg graft 42ccdea3bb16 32af7686d403
125 grafting revision 1
125 grafting revision 1
126 grafting revision 3
126 grafting revision 3
127 $ hg rebase -s 42ccdea3bb16 -d .
127 $ hg rebase -s 42ccdea3bb16 -d .
128 $ hg log -G
128 $ hg log -G
129 @ 10:5ae4c968c6ac C
129 @ 10:5ae4c968c6ac C
130 |
130 |
131 o 9:08483444fef9 D
131 o 9:08483444fef9 D
132 |
132 |
133 o 8:8877864f1edb B
133 o 8:8877864f1edb B
134 |
134 |
135 | o 7:02de42196ebe H
135 | o 7:02de42196ebe H
136 | |
136 | |
137 o | 6:eea13746799a G
137 o | 6:eea13746799a G
138 |\|
138 |\|
139 | o 5:24b6387c8c8c F
139 | o 5:24b6387c8c8c F
140 | |
140 | |
141 o | 4:9520eea781bc E
141 o | 4:9520eea781bc E
142 |/
142 |/
143 o 0:cd010b8cd998 A
143 o 0:cd010b8cd998 A
144
144
145 $ hg log --hidden -G
145 $ hg log --hidden -G
146 @ 10:5ae4c968c6ac C
146 @ 10:5ae4c968c6ac C
147 |
147 |
148 o 9:08483444fef9 D
148 o 9:08483444fef9 D
149 |
149 |
150 o 8:8877864f1edb B
150 o 8:8877864f1edb B
151 |
151 |
152 | o 7:02de42196ebe H
152 | o 7:02de42196ebe H
153 | |
153 | |
154 o | 6:eea13746799a G
154 o | 6:eea13746799a G
155 |\|
155 |\|
156 | o 5:24b6387c8c8c F
156 | o 5:24b6387c8c8c F
157 | |
157 | |
158 o | 4:9520eea781bc E
158 o | 4:9520eea781bc E
159 |/
159 |/
160 | x 3:32af7686d403 D
160 | x 3:32af7686d403 D
161 | |
161 | |
162 | x 2:5fddd98957c8 C
162 | x 2:5fddd98957c8 C
163 | |
163 | |
164 | x 1:42ccdea3bb16 B
164 | x 1:42ccdea3bb16 B
165 |/
165 |/
166 o 0:cd010b8cd998 A
166 o 0:cd010b8cd998 A
167
167
168 $ hg debugobsolete
168 $ hg debugobsolete
169 42ccdea3bb16d28e1848c95fe2e44c000f3f21b1 08483444fef91d6224f6655ee586a65d263ad34c 0 {'date': '*', 'user': 'test'} (glob)
169 42ccdea3bb16d28e1848c95fe2e44c000f3f21b1 08483444fef91d6224f6655ee586a65d263ad34c 0 {'date': '*', 'user': 'test'} (glob)
170 5fddd98957c8a54a4d436dfe1da9d87f21a1b97b 5ae4c968c6aca831df823664e706c9d4aa34473d 0 {'date': '*', 'user': 'test'} (glob)
170 5fddd98957c8a54a4d436dfe1da9d87f21a1b97b 5ae4c968c6aca831df823664e706c9d4aa34473d 0 {'date': '*', 'user': 'test'} (glob)
171 32af7686d403cf45b5d95f2d70cebea587ac806a 5ae4c968c6aca831df823664e706c9d4aa34473d 0 {'date': '*', 'user': 'test'} (glob)
171 32af7686d403cf45b5d95f2d70cebea587ac806a 5ae4c968c6aca831df823664e706c9d4aa34473d 0 {'date': '*', 'user': 'test'} (glob)
172
172
173
173
174 $ cd ..
174 $ cd ..
175
175
176 collapse rebase
176 collapse rebase
177 ---------------------------------
177 ---------------------------------
178
178
179 $ hg clone base collapse
179 $ hg clone base collapse
180 updating to branch default
180 updating to branch default
181 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
181 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
182 $ cd collapse
182 $ cd collapse
183 $ hg rebase -s 42ccdea3bb16 -d eea13746799a --collapse
183 $ hg rebase -s 42ccdea3bb16 -d eea13746799a --collapse
184 $ hg log -G
184 $ hg log -G
185 @ 8:4dc2197e807b Collapsed revision
185 @ 8:4dc2197e807b Collapsed revision
186 |
186 |
187 | o 7:02de42196ebe H
187 | o 7:02de42196ebe H
188 | |
188 | |
189 o | 6:eea13746799a G
189 o | 6:eea13746799a G
190 |\|
190 |\|
191 | o 5:24b6387c8c8c F
191 | o 5:24b6387c8c8c F
192 | |
192 | |
193 o | 4:9520eea781bc E
193 o | 4:9520eea781bc E
194 |/
194 |/
195 o 0:cd010b8cd998 A
195 o 0:cd010b8cd998 A
196
196
197 $ hg log --hidden -G
197 $ hg log --hidden -G
198 @ 8:4dc2197e807b Collapsed revision
198 @ 8:4dc2197e807b Collapsed revision
199 |
199 |
200 | o 7:02de42196ebe H
200 | o 7:02de42196ebe H
201 | |
201 | |
202 o | 6:eea13746799a G
202 o | 6:eea13746799a G
203 |\|
203 |\|
204 | o 5:24b6387c8c8c F
204 | o 5:24b6387c8c8c F
205 | |
205 | |
206 o | 4:9520eea781bc E
206 o | 4:9520eea781bc E
207 |/
207 |/
208 | x 3:32af7686d403 D
208 | x 3:32af7686d403 D
209 | |
209 | |
210 | x 2:5fddd98957c8 C
210 | x 2:5fddd98957c8 C
211 | |
211 | |
212 | x 1:42ccdea3bb16 B
212 | x 1:42ccdea3bb16 B
213 |/
213 |/
214 o 0:cd010b8cd998 A
214 o 0:cd010b8cd998 A
215
215
216 $ hg id --debug
216 $ hg id --debug
217 4dc2197e807bae9817f09905b50ab288be2dbbcf tip
217 4dc2197e807bae9817f09905b50ab288be2dbbcf tip
218 $ hg debugobsolete
218 $ hg debugobsolete
219 42ccdea3bb16d28e1848c95fe2e44c000f3f21b1 4dc2197e807bae9817f09905b50ab288be2dbbcf 0 {'date': '*', 'user': 'test'} (glob)
219 42ccdea3bb16d28e1848c95fe2e44c000f3f21b1 4dc2197e807bae9817f09905b50ab288be2dbbcf 0 {'date': '*', 'user': 'test'} (glob)
220 5fddd98957c8a54a4d436dfe1da9d87f21a1b97b 4dc2197e807bae9817f09905b50ab288be2dbbcf 0 {'date': '*', 'user': 'test'} (glob)
220 5fddd98957c8a54a4d436dfe1da9d87f21a1b97b 4dc2197e807bae9817f09905b50ab288be2dbbcf 0 {'date': '*', 'user': 'test'} (glob)
221 32af7686d403cf45b5d95f2d70cebea587ac806a 4dc2197e807bae9817f09905b50ab288be2dbbcf 0 {'date': '*', 'user': 'test'} (glob)
221 32af7686d403cf45b5d95f2d70cebea587ac806a 4dc2197e807bae9817f09905b50ab288be2dbbcf 0 {'date': '*', 'user': 'test'} (glob)
222
222
223 $ cd ..
223 $ cd ..
224
224
225 Rebase set has hidden descendants
225 Rebase set has hidden descendants
226 ---------------------------------
226 ---------------------------------
227
227
228 We rebase a changeset which has a hidden changeset. The hidden changeset must
228 We rebase a changeset which has a hidden changeset. The hidden changeset must
229 not be rebased.
229 not be rebased.
230
230
231 $ hg clone base hidden
231 $ hg clone base hidden
232 updating to branch default
232 updating to branch default
233 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
233 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
234 $ cd hidden
234 $ cd hidden
235 $ hg rebase -s 5fddd98957c8 -d eea13746799a
235 $ hg rebase -s 5fddd98957c8 -d eea13746799a
236 $ hg rebase -s 42ccdea3bb16 -d 02de42196ebe
236 $ hg rebase -s 42ccdea3bb16 -d 02de42196ebe
237 $ hg log -G
237 $ hg log -G
238 @ 10:7c6027df6a99 B
238 @ 10:7c6027df6a99 B
239 |
239 |
240 | o 9:cf44d2f5a9f4 D
240 | o 9:cf44d2f5a9f4 D
241 | |
241 | |
242 | o 8:e273c5e7d2d2 C
242 | o 8:e273c5e7d2d2 C
243 | |
243 | |
244 o | 7:02de42196ebe H
244 o | 7:02de42196ebe H
245 | |
245 | |
246 | o 6:eea13746799a G
246 | o 6:eea13746799a G
247 |/|
247 |/|
248 o | 5:24b6387c8c8c F
248 o | 5:24b6387c8c8c F
249 | |
249 | |
250 | o 4:9520eea781bc E
250 | o 4:9520eea781bc E
251 |/
251 |/
252 o 0:cd010b8cd998 A
252 o 0:cd010b8cd998 A
253
253
254 $ hg log --hidden -G
254 $ hg log --hidden -G
255 @ 10:7c6027df6a99 B
255 @ 10:7c6027df6a99 B
256 |
256 |
257 | o 9:cf44d2f5a9f4 D
257 | o 9:cf44d2f5a9f4 D
258 | |
258 | |
259 | o 8:e273c5e7d2d2 C
259 | o 8:e273c5e7d2d2 C
260 | |
260 | |
261 o | 7:02de42196ebe H
261 o | 7:02de42196ebe H
262 | |
262 | |
263 | o 6:eea13746799a G
263 | o 6:eea13746799a G
264 |/|
264 |/|
265 o | 5:24b6387c8c8c F
265 o | 5:24b6387c8c8c F
266 | |
266 | |
267 | o 4:9520eea781bc E
267 | o 4:9520eea781bc E
268 |/
268 |/
269 | x 3:32af7686d403 D
269 | x 3:32af7686d403 D
270 | |
270 | |
271 | x 2:5fddd98957c8 C
271 | x 2:5fddd98957c8 C
272 | |
272 | |
273 | x 1:42ccdea3bb16 B
273 | x 1:42ccdea3bb16 B
274 |/
274 |/
275 o 0:cd010b8cd998 A
275 o 0:cd010b8cd998 A
276
276
277 $ hg debugobsolete
277 $ hg debugobsolete
278 5fddd98957c8a54a4d436dfe1da9d87f21a1b97b e273c5e7d2d29df783dce9f9eaa3ac4adc69c15d 0 {'date': '*', 'user': 'test'} (glob)
278 5fddd98957c8a54a4d436dfe1da9d87f21a1b97b e273c5e7d2d29df783dce9f9eaa3ac4adc69c15d 0 {'date': '*', 'user': 'test'} (glob)
279 32af7686d403cf45b5d95f2d70cebea587ac806a cf44d2f5a9f4297a62be94cbdd3dff7c7dc54258 0 {'date': '*', 'user': 'test'} (glob)
279 32af7686d403cf45b5d95f2d70cebea587ac806a cf44d2f5a9f4297a62be94cbdd3dff7c7dc54258 0 {'date': '*', 'user': 'test'} (glob)
280 42ccdea3bb16d28e1848c95fe2e44c000f3f21b1 7c6027df6a99d93f461868e5433f63bde20b6dfb 0 {'date': '*', 'user': 'test'} (glob)
280 42ccdea3bb16d28e1848c95fe2e44c000f3f21b1 7c6027df6a99d93f461868e5433f63bde20b6dfb 0 {'date': '*', 'user': 'test'} (glob)
281
281
282 Test that rewriting leaving instability behind is allowed
282 Test that rewriting leaving instability behind is allowed
283 ---------------------------------------------------------------------
283 ---------------------------------------------------------------------
284
284
285 $ hg log -r 'children(8)'
285 $ hg log -r 'children(8)'
286 9:cf44d2f5a9f4 D (no-eol)
286 9:cf44d2f5a9f4 D (no-eol)
287 $ hg rebase -r 8
287 $ hg rebase -r 8
288 $ hg log -G
288 $ hg log -G
289 @ 11:0d8f238b634c C
289 @ 11:0d8f238b634c C
290 |
290 |
291 o 10:7c6027df6a99 B
291 o 10:7c6027df6a99 B
292 |
292 |
293 | o 9:cf44d2f5a9f4 D
293 | o 9:cf44d2f5a9f4 D
294 | |
294 | |
295 | x 8:e273c5e7d2d2 C
295 | x 8:e273c5e7d2d2 C
296 | |
296 | |
297 o | 7:02de42196ebe H
297 o | 7:02de42196ebe H
298 | |
298 | |
299 | o 6:eea13746799a G
299 | o 6:eea13746799a G
300 |/|
300 |/|
301 o | 5:24b6387c8c8c F
301 o | 5:24b6387c8c8c F
302 | |
302 | |
303 | o 4:9520eea781bc E
303 | o 4:9520eea781bc E
304 |/
304 |/
305 o 0:cd010b8cd998 A
305 o 0:cd010b8cd998 A
306
306
307
307
308
308
309 Test multiple root handling
310 ------------------------------------
311
312 $ hg rebase --dest 4 --rev '7+11+9'
313 $ hg log -G
314 @ 14:00891d85fcfc C
315 |
316 | o 13:102b4c1d889b D
317 |/
318 | o 12:bfe264faf697 H
319 |/
320 | o 10:7c6027df6a99 B
321 | |
322 | x 7:02de42196ebe H
323 | |
324 +---o 6:eea13746799a G
325 | |/
326 | o 5:24b6387c8c8c F
327 | |
328 o | 4:9520eea781bc E
329 |/
330 o 0:cd010b8cd998 A
331
@@ -1,547 +1,649 b''
1 $ cat >> $HGRCPATH <<EOF
1 $ cat >> $HGRCPATH <<EOF
2 > [extensions]
2 > [extensions]
3 > graphlog=
3 > graphlog=
4 > rebase=
4 > rebase=
5 >
5 >
6 > [phases]
6 > [phases]
7 > publish=False
7 > publish=False
8 >
8 >
9 > [alias]
9 > [alias]
10 > tglog = log -G --template "{rev}: '{desc}' {branches}\n"
10 > tglog = log -G --template "{rev}: '{desc}' {branches}\n"
11 > EOF
11 > EOF
12
12
13
13
14 $ hg init a
14 $ hg init a
15 $ cd a
15 $ cd a
16 $ hg unbundle "$TESTDIR/bundles/rebase.hg"
16 $ hg unbundle "$TESTDIR/bundles/rebase.hg"
17 adding changesets
17 adding changesets
18 adding manifests
18 adding manifests
19 adding file changes
19 adding file changes
20 added 8 changesets with 7 changes to 7 files (+2 heads)
20 added 8 changesets with 7 changes to 7 files (+2 heads)
21 (run 'hg heads' to see heads, 'hg merge' to merge)
21 (run 'hg heads' to see heads, 'hg merge' to merge)
22 $ hg up tip
22 $ hg up tip
23 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
23 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
24 $ cd ..
24 $ cd ..
25
25
26
26
27 Rebasing
27 Rebasing
28 D onto H - simple rebase:
28 D onto H - simple rebase:
29
29
30 $ hg clone -q -u . a a1
30 $ hg clone -q -u . a a1
31 $ cd a1
31 $ cd a1
32
32
33 $ hg tglog
33 $ hg tglog
34 @ 7: 'H'
34 @ 7: 'H'
35 |
35 |
36 | o 6: 'G'
36 | o 6: 'G'
37 |/|
37 |/|
38 o | 5: 'F'
38 o | 5: 'F'
39 | |
39 | |
40 | o 4: 'E'
40 | o 4: 'E'
41 |/
41 |/
42 | o 3: 'D'
42 | o 3: 'D'
43 | |
43 | |
44 | o 2: 'C'
44 | o 2: 'C'
45 | |
45 | |
46 | o 1: 'B'
46 | o 1: 'B'
47 |/
47 |/
48 o 0: 'A'
48 o 0: 'A'
49
49
50
50
51 $ hg rebase -s 3 -d 7
51 $ hg rebase -s 3 -d 7
52 saved backup bundle to $TESTTMP/a1/.hg/strip-backup/*-backup.hg (glob)
52 saved backup bundle to $TESTTMP/a1/.hg/strip-backup/*-backup.hg (glob)
53
53
54 $ hg tglog
54 $ hg tglog
55 @ 7: 'D'
55 @ 7: 'D'
56 |
56 |
57 o 6: 'H'
57 o 6: 'H'
58 |
58 |
59 | o 5: 'G'
59 | o 5: 'G'
60 |/|
60 |/|
61 o | 4: 'F'
61 o | 4: 'F'
62 | |
62 | |
63 | o 3: 'E'
63 | o 3: 'E'
64 |/
64 |/
65 | o 2: 'C'
65 | o 2: 'C'
66 | |
66 | |
67 | o 1: 'B'
67 | o 1: 'B'
68 |/
68 |/
69 o 0: 'A'
69 o 0: 'A'
70
70
71 $ cd ..
71 $ cd ..
72
72
73
73
74 D onto F - intermediate point:
74 D onto F - intermediate point:
75
75
76 $ hg clone -q -u . a a2
76 $ hg clone -q -u . a a2
77 $ cd a2
77 $ cd a2
78
78
79 $ hg rebase -s 3 -d 5
79 $ hg rebase -s 3 -d 5
80 saved backup bundle to $TESTTMP/a2/.hg/strip-backup/*-backup.hg (glob)
80 saved backup bundle to $TESTTMP/a2/.hg/strip-backup/*-backup.hg (glob)
81
81
82 $ hg tglog
82 $ hg tglog
83 @ 7: 'D'
83 @ 7: 'D'
84 |
84 |
85 | o 6: 'H'
85 | o 6: 'H'
86 |/
86 |/
87 | o 5: 'G'
87 | o 5: 'G'
88 |/|
88 |/|
89 o | 4: 'F'
89 o | 4: 'F'
90 | |
90 | |
91 | o 3: 'E'
91 | o 3: 'E'
92 |/
92 |/
93 | o 2: 'C'
93 | o 2: 'C'
94 | |
94 | |
95 | o 1: 'B'
95 | o 1: 'B'
96 |/
96 |/
97 o 0: 'A'
97 o 0: 'A'
98
98
99 $ cd ..
99 $ cd ..
100
100
101
101
102 E onto H - skip of G:
102 E onto H - skip of G:
103
103
104 $ hg clone -q -u . a a3
104 $ hg clone -q -u . a a3
105 $ cd a3
105 $ cd a3
106
106
107 $ hg rebase -s 4 -d 7
107 $ hg rebase -s 4 -d 7
108 saved backup bundle to $TESTTMP/a3/.hg/strip-backup/*-backup.hg (glob)
108 saved backup bundle to $TESTTMP/a3/.hg/strip-backup/*-backup.hg (glob)
109
109
110 $ hg tglog
110 $ hg tglog
111 @ 6: 'E'
111 @ 6: 'E'
112 |
112 |
113 o 5: 'H'
113 o 5: 'H'
114 |
114 |
115 o 4: 'F'
115 o 4: 'F'
116 |
116 |
117 | o 3: 'D'
117 | o 3: 'D'
118 | |
118 | |
119 | o 2: 'C'
119 | o 2: 'C'
120 | |
120 | |
121 | o 1: 'B'
121 | o 1: 'B'
122 |/
122 |/
123 o 0: 'A'
123 o 0: 'A'
124
124
125 $ cd ..
125 $ cd ..
126
126
127
127
128 F onto E - rebase of a branching point (skip G):
128 F onto E - rebase of a branching point (skip G):
129
129
130 $ hg clone -q -u . a a4
130 $ hg clone -q -u . a a4
131 $ cd a4
131 $ cd a4
132
132
133 $ hg rebase -s 5 -d 4
133 $ hg rebase -s 5 -d 4
134 saved backup bundle to $TESTTMP/a4/.hg/strip-backup/*-backup.hg (glob)
134 saved backup bundle to $TESTTMP/a4/.hg/strip-backup/*-backup.hg (glob)
135
135
136 $ hg tglog
136 $ hg tglog
137 @ 6: 'H'
137 @ 6: 'H'
138 |
138 |
139 o 5: 'F'
139 o 5: 'F'
140 |
140 |
141 o 4: 'E'
141 o 4: 'E'
142 |
142 |
143 | o 3: 'D'
143 | o 3: 'D'
144 | |
144 | |
145 | o 2: 'C'
145 | o 2: 'C'
146 | |
146 | |
147 | o 1: 'B'
147 | o 1: 'B'
148 |/
148 |/
149 o 0: 'A'
149 o 0: 'A'
150
150
151 $ cd ..
151 $ cd ..
152
152
153
153
154 G onto H - merged revision having a parent in ancestors of target:
154 G onto H - merged revision having a parent in ancestors of target:
155
155
156 $ hg clone -q -u . a a5
156 $ hg clone -q -u . a a5
157 $ cd a5
157 $ cd a5
158
158
159 $ hg rebase -s 6 -d 7
159 $ hg rebase -s 6 -d 7
160 saved backup bundle to $TESTTMP/a5/.hg/strip-backup/*-backup.hg (glob)
160 saved backup bundle to $TESTTMP/a5/.hg/strip-backup/*-backup.hg (glob)
161
161
162 $ hg tglog
162 $ hg tglog
163 @ 7: 'G'
163 @ 7: 'G'
164 |\
164 |\
165 | o 6: 'H'
165 | o 6: 'H'
166 | |
166 | |
167 | o 5: 'F'
167 | o 5: 'F'
168 | |
168 | |
169 o | 4: 'E'
169 o | 4: 'E'
170 |/
170 |/
171 | o 3: 'D'
171 | o 3: 'D'
172 | |
172 | |
173 | o 2: 'C'
173 | o 2: 'C'
174 | |
174 | |
175 | o 1: 'B'
175 | o 1: 'B'
176 |/
176 |/
177 o 0: 'A'
177 o 0: 'A'
178
178
179 $ cd ..
179 $ cd ..
180
180
181
181
182 F onto B - G maintains E as parent:
182 F onto B - G maintains E as parent:
183
183
184 $ hg clone -q -u . a a6
184 $ hg clone -q -u . a a6
185 $ cd a6
185 $ cd a6
186
186
187 $ hg rebase -s 5 -d 1
187 $ hg rebase -s 5 -d 1
188 saved backup bundle to $TESTTMP/a6/.hg/strip-backup/*-backup.hg (glob)
188 saved backup bundle to $TESTTMP/a6/.hg/strip-backup/*-backup.hg (glob)
189
189
190 $ hg tglog
190 $ hg tglog
191 @ 7: 'H'
191 @ 7: 'H'
192 |
192 |
193 | o 6: 'G'
193 | o 6: 'G'
194 |/|
194 |/|
195 o | 5: 'F'
195 o | 5: 'F'
196 | |
196 | |
197 | o 4: 'E'
197 | o 4: 'E'
198 | |
198 | |
199 | | o 3: 'D'
199 | | o 3: 'D'
200 | | |
200 | | |
201 +---o 2: 'C'
201 +---o 2: 'C'
202 | |
202 | |
203 o | 1: 'B'
203 o | 1: 'B'
204 |/
204 |/
205 o 0: 'A'
205 o 0: 'A'
206
206
207 $ cd ..
207 $ cd ..
208
208
209
209
210 These will fail (using --source):
210 These will fail (using --source):
211
211
212 G onto F - rebase onto an ancestor:
212 G onto F - rebase onto an ancestor:
213
213
214 $ hg clone -q -u . a a7
214 $ hg clone -q -u . a a7
215 $ cd a7
215 $ cd a7
216
216
217 $ hg rebase -s 6 -d 5
217 $ hg rebase -s 6 -d 5
218 nothing to rebase
218 nothing to rebase
219 [1]
219 [1]
220
220
221 F onto G - rebase onto a descendant:
221 F onto G - rebase onto a descendant:
222
222
223 $ hg rebase -s 5 -d 6
223 $ hg rebase -s 5 -d 6
224 abort: source is ancestor of destination
224 abort: source is ancestor of destination
225 [255]
225 [255]
226
226
227 G onto B - merge revision with both parents not in ancestors of target:
227 G onto B - merge revision with both parents not in ancestors of target:
228
228
229 $ hg rebase -s 6 -d 1
229 $ hg rebase -s 6 -d 1
230 abort: cannot use revision 6 as base, result would have 3 parents
230 abort: cannot use revision 6 as base, result would have 3 parents
231 [255]
231 [255]
232
232
233
233
234 These will abort gracefully (using --base):
234 These will abort gracefully (using --base):
235
235
236 G onto G - rebase onto same changeset:
236 G onto G - rebase onto same changeset:
237
237
238 $ hg rebase -b 6 -d 6
238 $ hg rebase -b 6 -d 6
239 nothing to rebase
239 nothing to rebase
240 [1]
240 [1]
241
241
242 G onto F - rebase onto an ancestor:
242 G onto F - rebase onto an ancestor:
243
243
244 $ hg rebase -b 6 -d 5
244 $ hg rebase -b 6 -d 5
245 nothing to rebase
245 nothing to rebase
246 [1]
246 [1]
247
247
248 F onto G - rebase onto a descendant:
248 F onto G - rebase onto a descendant:
249
249
250 $ hg rebase -b 5 -d 6
250 $ hg rebase -b 5 -d 6
251 nothing to rebase
251 nothing to rebase
252 [1]
252 [1]
253
253
254 C onto A - rebase onto an ancestor:
254 C onto A - rebase onto an ancestor:
255
255
256 $ hg rebase -d 0 -s 2
256 $ hg rebase -d 0 -s 2
257 saved backup bundle to $TESTTMP/a7/.hg/strip-backup/5fddd98957c8-backup.hg (glob)
257 saved backup bundle to $TESTTMP/a7/.hg/strip-backup/5fddd98957c8-backup.hg (glob)
258 $ hg tglog
258 $ hg tglog
259 @ 7: 'D'
259 @ 7: 'D'
260 |
260 |
261 o 6: 'C'
261 o 6: 'C'
262 |
262 |
263 | o 5: 'H'
263 | o 5: 'H'
264 | |
264 | |
265 | | o 4: 'G'
265 | | o 4: 'G'
266 | |/|
266 | |/|
267 | o | 3: 'F'
267 | o | 3: 'F'
268 |/ /
268 |/ /
269 | o 2: 'E'
269 | o 2: 'E'
270 |/
270 |/
271 | o 1: 'B'
271 | o 1: 'B'
272 |/
272 |/
273 o 0: 'A'
273 o 0: 'A'
274
274
275
275
276 Check rebasing public changeset
276 Check rebasing public changeset
277
277
278 $ hg pull --config phases.publish=True -q -r 6 . # update phase of 6
278 $ hg pull --config phases.publish=True -q -r 6 . # update phase of 6
279 $ hg rebase -d 5 -b 6
279 $ hg rebase -d 5 -b 6
280 abort: can't rebase immutable changeset e1c4361dd923
280 abort: can't rebase immutable changeset e1c4361dd923
281 (see hg help phases for details)
281 (see hg help phases for details)
282 [255]
282 [255]
283
283
284 $ hg rebase -d 5 -b 6 --keep
284 $ hg rebase -d 5 -b 6 --keep
285
285
286 Check rebasing mutable changeset
286 Check rebasing mutable changeset
287 Source phase greater or equal to destination phase: new changeset get the phase of source:
287 Source phase greater or equal to destination phase: new changeset get the phase of source:
288 $ hg rebase -s9 -d0
288 $ hg rebase -s9 -d0
289 saved backup bundle to $TESTTMP/a7/.hg/strip-backup/2b23e52411f4-backup.hg (glob)
289 saved backup bundle to $TESTTMP/a7/.hg/strip-backup/2b23e52411f4-backup.hg (glob)
290 $ hg log --template "{phase}\n" -r 9
290 $ hg log --template "{phase}\n" -r 9
291 draft
291 draft
292 $ hg rebase -s9 -d1
292 $ hg rebase -s9 -d1
293 saved backup bundle to $TESTTMP/a7/.hg/strip-backup/2cb10d0cfc6c-backup.hg (glob)
293 saved backup bundle to $TESTTMP/a7/.hg/strip-backup/2cb10d0cfc6c-backup.hg (glob)
294 $ hg log --template "{phase}\n" -r 9
294 $ hg log --template "{phase}\n" -r 9
295 draft
295 draft
296 $ hg phase --force --secret 9
296 $ hg phase --force --secret 9
297 $ hg rebase -s9 -d0
297 $ hg rebase -s9 -d0
298 saved backup bundle to $TESTTMP/a7/.hg/strip-backup/c5b12b67163a-backup.hg (glob)
298 saved backup bundle to $TESTTMP/a7/.hg/strip-backup/c5b12b67163a-backup.hg (glob)
299 $ hg log --template "{phase}\n" -r 9
299 $ hg log --template "{phase}\n" -r 9
300 secret
300 secret
301 $ hg rebase -s9 -d1
301 $ hg rebase -s9 -d1
302 saved backup bundle to $TESTTMP/a7/.hg/strip-backup/2a0524f868ac-backup.hg (glob)
302 saved backup bundle to $TESTTMP/a7/.hg/strip-backup/2a0524f868ac-backup.hg (glob)
303 $ hg log --template "{phase}\n" -r 9
303 $ hg log --template "{phase}\n" -r 9
304 secret
304 secret
305 Source phase lower than destination phase: new changeset get the phase of destination:
305 Source phase lower than destination phase: new changeset get the phase of destination:
306 $ hg rebase -s8 -d9
306 $ hg rebase -s8 -d9
307 saved backup bundle to $TESTTMP/a7/.hg/strip-backup/6d4f22462821-backup.hg (glob)
307 saved backup bundle to $TESTTMP/a7/.hg/strip-backup/6d4f22462821-backup.hg (glob)
308 $ hg log --template "{phase}\n" -r 'rev(9)'
308 $ hg log --template "{phase}\n" -r 'rev(9)'
309 secret
309 secret
310
310
311 $ cd ..
311 $ cd ..
312
312
313 Test for revset
313 Test for revset
314
314
315 We need a bit different graph
315 We need a bit different graph
316 All destination are B
316 All destination are B
317
317
318 $ hg init ah
318 $ hg init ah
319 $ cd ah
319 $ cd ah
320 $ hg unbundle "$TESTDIR/bundles/rebase-revset.hg"
320 $ hg unbundle "$TESTDIR/bundles/rebase-revset.hg"
321 adding changesets
321 adding changesets
322 adding manifests
322 adding manifests
323 adding file changes
323 adding file changes
324 added 9 changesets with 9 changes to 9 files (+2 heads)
324 added 9 changesets with 9 changes to 9 files (+2 heads)
325 (run 'hg heads' to see heads, 'hg merge' to merge)
325 (run 'hg heads' to see heads, 'hg merge' to merge)
326 $ hg tglog
326 $ hg tglog
327 o 8: 'I'
327 o 8: 'I'
328 |
328 |
329 o 7: 'H'
329 o 7: 'H'
330 |
330 |
331 o 6: 'G'
331 o 6: 'G'
332 |
332 |
333 | o 5: 'F'
333 | o 5: 'F'
334 | |
334 | |
335 | o 4: 'E'
335 | o 4: 'E'
336 |/
336 |/
337 o 3: 'D'
337 o 3: 'D'
338 |
338 |
339 o 2: 'C'
339 o 2: 'C'
340 |
340 |
341 | o 1: 'B'
341 | o 1: 'B'
342 |/
342 |/
343 o 0: 'A'
343 o 0: 'A'
344
344
345 $ cd ..
345 $ cd ..
346
346
347
347
348 Simple case with keep:
348 Simple case with keep:
349
349
350 Source on have two descendant heads but ask for one
350 Source on have two descendant heads but ask for one
351
351
352 $ hg clone -q -u . ah ah1
352 $ hg clone -q -u . ah ah1
353 $ cd ah1
353 $ cd ah1
354 $ hg rebase -r '2::8' -d 1
354 $ hg rebase -r '2::8' -d 1
355 abort: can't remove original changesets with unrebased descendants
355 abort: can't remove original changesets with unrebased descendants
356 (use --keep to keep original changesets)
356 (use --keep to keep original changesets)
357 [255]
357 [255]
358 $ hg rebase -r '2::8' -d 1 --keep
358 $ hg rebase -r '2::8' -d 1 --keep
359 $ hg tglog
359 $ hg tglog
360 @ 13: 'I'
360 @ 13: 'I'
361 |
361 |
362 o 12: 'H'
362 o 12: 'H'
363 |
363 |
364 o 11: 'G'
364 o 11: 'G'
365 |
365 |
366 o 10: 'D'
366 o 10: 'D'
367 |
367 |
368 o 9: 'C'
368 o 9: 'C'
369 |
369 |
370 | o 8: 'I'
370 | o 8: 'I'
371 | |
371 | |
372 | o 7: 'H'
372 | o 7: 'H'
373 | |
373 | |
374 | o 6: 'G'
374 | o 6: 'G'
375 | |
375 | |
376 | | o 5: 'F'
376 | | o 5: 'F'
377 | | |
377 | | |
378 | | o 4: 'E'
378 | | o 4: 'E'
379 | |/
379 | |/
380 | o 3: 'D'
380 | o 3: 'D'
381 | |
381 | |
382 | o 2: 'C'
382 | o 2: 'C'
383 | |
383 | |
384 o | 1: 'B'
384 o | 1: 'B'
385 |/
385 |/
386 o 0: 'A'
386 o 0: 'A'
387
387
388
388
389 $ cd ..
389 $ cd ..
390
390
391 Base on have one descendant heads we ask for but common ancestor have two
391 Base on have one descendant heads we ask for but common ancestor have two
392
392
393 $ hg clone -q -u . ah ah2
393 $ hg clone -q -u . ah ah2
394 $ cd ah2
394 $ cd ah2
395 $ hg rebase -r '3::8' -d 1
395 $ hg rebase -r '3::8' -d 1
396 abort: can't remove original changesets with unrebased descendants
396 abort: can't remove original changesets with unrebased descendants
397 (use --keep to keep original changesets)
397 (use --keep to keep original changesets)
398 [255]
398 [255]
399 $ hg rebase -r '3::8' -d 1 --keep
399 $ hg rebase -r '3::8' -d 1 --keep
400 $ hg tglog
400 $ hg tglog
401 @ 12: 'I'
401 @ 12: 'I'
402 |
402 |
403 o 11: 'H'
403 o 11: 'H'
404 |
404 |
405 o 10: 'G'
405 o 10: 'G'
406 |
406 |
407 o 9: 'D'
407 o 9: 'D'
408 |
408 |
409 | o 8: 'I'
409 | o 8: 'I'
410 | |
410 | |
411 | o 7: 'H'
411 | o 7: 'H'
412 | |
412 | |
413 | o 6: 'G'
413 | o 6: 'G'
414 | |
414 | |
415 | | o 5: 'F'
415 | | o 5: 'F'
416 | | |
416 | | |
417 | | o 4: 'E'
417 | | o 4: 'E'
418 | |/
418 | |/
419 | o 3: 'D'
419 | o 3: 'D'
420 | |
420 | |
421 | o 2: 'C'
421 | o 2: 'C'
422 | |
422 | |
423 o | 1: 'B'
423 o | 1: 'B'
424 |/
424 |/
425 o 0: 'A'
425 o 0: 'A'
426
426
427
427
428 $ cd ..
428 $ cd ..
429
429
430 rebase subset
430 rebase subset
431
431
432 $ hg clone -q -u . ah ah3
432 $ hg clone -q -u . ah ah3
433 $ cd ah3
433 $ cd ah3
434 $ hg rebase -r '3::7' -d 1
434 $ hg rebase -r '3::7' -d 1
435 abort: can't remove original changesets with unrebased descendants
435 abort: can't remove original changesets with unrebased descendants
436 (use --keep to keep original changesets)
436 (use --keep to keep original changesets)
437 [255]
437 [255]
438 $ hg rebase -r '3::7' -d 1 --keep
438 $ hg rebase -r '3::7' -d 1 --keep
439 $ hg tglog
439 $ hg tglog
440 @ 11: 'H'
440 @ 11: 'H'
441 |
441 |
442 o 10: 'G'
442 o 10: 'G'
443 |
443 |
444 o 9: 'D'
444 o 9: 'D'
445 |
445 |
446 | o 8: 'I'
446 | o 8: 'I'
447 | |
447 | |
448 | o 7: 'H'
448 | o 7: 'H'
449 | |
449 | |
450 | o 6: 'G'
450 | o 6: 'G'
451 | |
451 | |
452 | | o 5: 'F'
452 | | o 5: 'F'
453 | | |
453 | | |
454 | | o 4: 'E'
454 | | o 4: 'E'
455 | |/
455 | |/
456 | o 3: 'D'
456 | o 3: 'D'
457 | |
457 | |
458 | o 2: 'C'
458 | o 2: 'C'
459 | |
459 | |
460 o | 1: 'B'
460 o | 1: 'B'
461 |/
461 |/
462 o 0: 'A'
462 o 0: 'A'
463
463
464
464
465 $ cd ..
465 $ cd ..
466
466
467 rebase subset with multiple head
467 rebase subset with multiple head
468
468
469 $ hg clone -q -u . ah ah4
469 $ hg clone -q -u . ah ah4
470 $ cd ah4
470 $ cd ah4
471 $ hg rebase -r '3::(7+5)' -d 1
471 $ hg rebase -r '3::(7+5)' -d 1
472 abort: can't remove original changesets with unrebased descendants
472 abort: can't remove original changesets with unrebased descendants
473 (use --keep to keep original changesets)
473 (use --keep to keep original changesets)
474 [255]
474 [255]
475 $ hg rebase -r '3::(7+5)' -d 1 --keep
475 $ hg rebase -r '3::(7+5)' -d 1 --keep
476 $ hg tglog
476 $ hg tglog
477 @ 13: 'H'
477 @ 13: 'H'
478 |
478 |
479 o 12: 'G'
479 o 12: 'G'
480 |
480 |
481 | o 11: 'F'
481 | o 11: 'F'
482 | |
482 | |
483 | o 10: 'E'
483 | o 10: 'E'
484 |/
484 |/
485 o 9: 'D'
485 o 9: 'D'
486 |
486 |
487 | o 8: 'I'
487 | o 8: 'I'
488 | |
488 | |
489 | o 7: 'H'
489 | o 7: 'H'
490 | |
490 | |
491 | o 6: 'G'
491 | o 6: 'G'
492 | |
492 | |
493 | | o 5: 'F'
493 | | o 5: 'F'
494 | | |
494 | | |
495 | | o 4: 'E'
495 | | o 4: 'E'
496 | |/
496 | |/
497 | o 3: 'D'
497 | o 3: 'D'
498 | |
498 | |
499 | o 2: 'C'
499 | o 2: 'C'
500 | |
500 | |
501 o | 1: 'B'
501 o | 1: 'B'
502 |/
502 |/
503 o 0: 'A'
503 o 0: 'A'
504
504
505
505
506 $ cd ..
506 $ cd ..
507
507
508 More advanced tests
508 More advanced tests
509
509
510 rebase on ancestor with revset
510 rebase on ancestor with revset
511
511
512 $ hg clone -q -u . ah ah5
512 $ hg clone -q -u . ah ah5
513 $ cd ah5
513 $ cd ah5
514 $ hg rebase -r '6::' -d 2
514 $ hg rebase -r '6::' -d 2
515 saved backup bundle to $TESTTMP/ah5/.hg/strip-backup/3d8a618087a7-backup.hg (glob)
515 saved backup bundle to $TESTTMP/ah5/.hg/strip-backup/3d8a618087a7-backup.hg (glob)
516 $ hg tglog
516 $ hg tglog
517 @ 8: 'I'
517 @ 8: 'I'
518 |
518 |
519 o 7: 'H'
519 o 7: 'H'
520 |
520 |
521 o 6: 'G'
521 o 6: 'G'
522 |
522 |
523 | o 5: 'F'
523 | o 5: 'F'
524 | |
524 | |
525 | o 4: 'E'
525 | o 4: 'E'
526 | |
526 | |
527 | o 3: 'D'
527 | o 3: 'D'
528 |/
528 |/
529 o 2: 'C'
529 o 2: 'C'
530 |
530 |
531 | o 1: 'B'
531 | o 1: 'B'
532 |/
532 |/
533 o 0: 'A'
533 o 0: 'A'
534
534
535 $ cd ..
535 $ cd ..
536
536
537
537
538 rebase with multiple root.
538 rebase with multiple root.
539 We rebase E and G on B
539 We rebase E and G on B
540 We would expect heads are I, F if it was supported
540 We would expect heads are I, F if it was supported
541
541
542 $ hg clone -q -u . ah ah6
542 $ hg clone -q -u . ah ah6
543 $ cd ah6
543 $ cd ah6
544 $ hg rebase -r '(4+6)::' -d 1
544 $ hg rebase -r '(4+6)::' -d 1
545 abort: can't rebase multiple roots
545 saved backup bundle to $TESTTMP/ah6/.hg/strip-backup/3d8a618087a7-backup.hg (glob)
546 [255]
546 $ hg tglog
547 @ 8: 'I'
548 |
549 o 7: 'H'
550 |
551 o 6: 'G'
552 |
553 | o 5: 'F'
554 | |
555 | o 4: 'E'
556 |/
557 | o 3: 'D'
558 | |
559 | o 2: 'C'
560 | |
561 o | 1: 'B'
562 |/
563 o 0: 'A'
564
547 $ cd ..
565 $ cd ..
566
567 More complexe rebase with multiple roots
568 each root have a different common ancestor with the destination and this is a detach
569
570 (setup)
571
572 $ hg clone -q -u . a a8
573 $ cd a8
574 $ echo I > I
575 $ hg add I
576 $ hg commit -m I
577 $ hg up 4
578 1 files updated, 0 files merged, 3 files removed, 0 files unresolved
579 $ echo I > J
580 $ hg add J
581 $ hg commit -m J
582 created new head
583 $ echo I > K
584 $ hg add K
585 $ hg commit -m K
586 $ hg tglog
587 @ 10: 'K'
588 |
589 o 9: 'J'
590 |
591 | o 8: 'I'
592 | |
593 | o 7: 'H'
594 | |
595 +---o 6: 'G'
596 | |/
597 | o 5: 'F'
598 | |
599 o | 4: 'E'
600 |/
601 | o 3: 'D'
602 | |
603 | o 2: 'C'
604 | |
605 | o 1: 'B'
606 |/
607 o 0: 'A'
608
609 (actual test)
610
611 $ hg rebase --dest 'desc(G)' --rev 'desc(K) + desc(I)'
612 saved backup bundle to $TESTTMP/a8/.hg/strip-backup/23a4ace37988-backup.hg (glob)
613 $ hg log --rev 'children(desc(G))'
614 changeset: 9:adb617877056
615 parent: 6:eea13746799a
616 user: test
617 date: Thu Jan 01 00:00:00 1970 +0000
618 summary: I
619
620 changeset: 10:882431a34a0e
621 tag: tip
622 parent: 6:eea13746799a
623 user: test
624 date: Thu Jan 01 00:00:00 1970 +0000
625 summary: K
626
627 $ hg tglog
628 @ 10: 'K'
629 |
630 | o 9: 'I'
631 |/
632 | o 8: 'J'
633 | |
634 | | o 7: 'H'
635 | | |
636 o---+ 6: 'G'
637 |/ /
638 | o 5: 'F'
639 | |
640 o | 4: 'E'
641 |/
642 | o 3: 'D'
643 | |
644 | o 2: 'C'
645 | |
646 | o 1: 'B'
647 |/
648 o 0: 'A'
649
General Comments 0
You need to be logged in to leave comments. Login now