##// END OF EJS Templates
rebase: use revset as soon as possible in internal logic...
Pierre-Yves David -
r15267:3bfdfefe default
parent child Browse files
Show More
@@ -1,613 +1,609
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
18 from mercurial import extensions, patch
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
29
30 @command('rebase',
30 @command('rebase',
31 [('s', 'source', '',
31 [('s', 'source', '',
32 _('rebase from the specified changeset'), _('REV')),
32 _('rebase from the specified changeset'), _('REV')),
33 ('b', 'base', '',
33 ('b', 'base', '',
34 _('rebase from the base of the specified changeset '
34 _('rebase from the base of the specified changeset '
35 '(up to greatest common ancestor of base and dest)'),
35 '(up to greatest common ancestor of base and dest)'),
36 _('REV')),
36 _('REV')),
37 ('d', 'dest', '',
37 ('d', 'dest', '',
38 _('rebase onto the specified changeset'), _('REV')),
38 _('rebase onto the specified changeset'), _('REV')),
39 ('', 'collapse', False, _('collapse the rebased changesets')),
39 ('', 'collapse', False, _('collapse the rebased changesets')),
40 ('m', 'message', '',
40 ('m', 'message', '',
41 _('use text as collapse commit message'), _('TEXT')),
41 _('use text as collapse commit message'), _('TEXT')),
42 ('e', 'edit', False, _('invoke editor on commit messages')),
42 ('e', 'edit', False, _('invoke editor on commit messages')),
43 ('l', 'logfile', '',
43 ('l', 'logfile', '',
44 _('read collapse commit message from file'), _('FILE')),
44 _('read collapse commit message from file'), _('FILE')),
45 ('', 'keep', False, _('keep original changesets')),
45 ('', 'keep', False, _('keep original changesets')),
46 ('', 'keepbranches', False, _('keep original branch names')),
46 ('', 'keepbranches', False, _('keep original branch names')),
47 ('', 'detach', False, _('force detaching of source from its original '
47 ('', 'detach', False, _('force detaching of source from its original '
48 'branch')),
48 'branch')),
49 ('t', 'tool', '', _('specify merge tool')),
49 ('t', 'tool', '', _('specify merge tool')),
50 ('c', 'continue', False, _('continue an interrupted rebase')),
50 ('c', 'continue', False, _('continue an interrupted rebase')),
51 ('a', 'abort', False, _('abort an interrupted rebase'))] +
51 ('a', 'abort', False, _('abort an interrupted rebase'))] +
52 templateopts,
52 templateopts,
53 _('hg rebase [-s REV | -b REV] [-d REV] [options]\n'
53 _('hg rebase [-s REV | -b REV] [-d REV] [options]\n'
54 'hg rebase {-a|-c}'))
54 'hg rebase {-a|-c}'))
55 def rebase(ui, repo, **opts):
55 def rebase(ui, repo, **opts):
56 """move changeset (and descendants) to a different branch
56 """move changeset (and descendants) to a different branch
57
57
58 Rebase uses repeated merging to graft changesets from one part of
58 Rebase uses repeated merging to graft changesets from one part of
59 history (the source) onto another (the destination). This can be
59 history (the source) onto another (the destination). This can be
60 useful for linearizing *local* changes relative to a master
60 useful for linearizing *local* changes relative to a master
61 development tree.
61 development tree.
62
62
63 You should not rebase changesets that have already been shared
63 You should not rebase changesets that have already been shared
64 with others. Doing so will force everybody else to perform the
64 with others. Doing so will force everybody else to perform the
65 same rebase or they will end up with duplicated changesets after
65 same rebase or they will end up with duplicated changesets after
66 pulling in your rebased changesets.
66 pulling in your rebased changesets.
67
67
68 If you don't specify a destination changeset (``-d/--dest``),
68 If you don't specify a destination changeset (``-d/--dest``),
69 rebase uses the tipmost head of the current named branch as the
69 rebase uses the tipmost head of the current named branch as the
70 destination. (The destination changeset is not modified by
70 destination. (The destination changeset is not modified by
71 rebasing, but new changesets are added as its descendants.)
71 rebasing, but new changesets are added as its descendants.)
72
72
73 You can specify which changesets to rebase in two ways: as a
73 You can specify which changesets to rebase in two ways: as a
74 "source" changeset or as a "base" changeset. Both are shorthand
74 "source" changeset or as a "base" changeset. Both are shorthand
75 for a topologically related set of changesets (the "source
75 for a topologically related set of changesets (the "source
76 branch"). If you specify source (``-s/--source``), rebase will
76 branch"). If you specify source (``-s/--source``), rebase will
77 rebase that changeset and all of its descendants onto dest. If you
77 rebase that changeset and all of its descendants onto dest. If you
78 specify base (``-b/--base``), rebase will select ancestors of base
78 specify base (``-b/--base``), rebase will select ancestors of base
79 back to but not including the common ancestor with dest. Thus,
79 back to but not including the common ancestor with dest. Thus,
80 ``-b`` is less precise but more convenient than ``-s``: you can
80 ``-b`` is less precise but more convenient than ``-s``: you can
81 specify any changeset in the source branch, and rebase will select
81 specify any changeset in the source branch, and rebase will select
82 the whole branch. If you specify neither ``-s`` nor ``-b``, rebase
82 the whole branch. If you specify neither ``-s`` nor ``-b``, rebase
83 uses the parent of the working directory as the base.
83 uses the parent of the working directory as the base.
84
84
85 By default, rebase recreates the changesets in the source branch
85 By default, rebase recreates the changesets in the source branch
86 as descendants of dest and then destroys the originals. Use
86 as descendants of dest and then destroys the originals. Use
87 ``--keep`` to preserve the original source changesets. Some
87 ``--keep`` to preserve the original source changesets. Some
88 changesets in the source branch (e.g. merges from the destination
88 changesets in the source branch (e.g. merges from the destination
89 branch) may be dropped if they no longer contribute any change.
89 branch) may be dropped if they no longer contribute any change.
90
90
91 One result of the rules for selecting the destination changeset
91 One result of the rules for selecting the destination changeset
92 and source branch is that, unlike ``merge``, rebase will do
92 and source branch is that, unlike ``merge``, rebase will do
93 nothing if you are at the latest (tipmost) head of a named branch
93 nothing if you are at the latest (tipmost) head of a named branch
94 with two heads. You need to explicitly specify source and/or
94 with two heads. You need to explicitly specify source and/or
95 destination (or ``update`` to the other head, if it's the head of
95 destination (or ``update`` to the other head, if it's the head of
96 the intended source branch).
96 the intended source branch).
97
97
98 If a rebase is interrupted to manually resolve a merge, it can be
98 If a rebase is interrupted to manually resolve a merge, it can be
99 continued with --continue/-c or aborted with --abort/-a.
99 continued with --continue/-c or aborted with --abort/-a.
100
100
101 Returns 0 on success, 1 if nothing to rebase.
101 Returns 0 on success, 1 if nothing to rebase.
102 """
102 """
103 originalwd = target = None
103 originalwd = target = None
104 external = nullrev
104 external = nullrev
105 state = {}
105 state = {}
106 skipped = set()
106 skipped = set()
107 targetancestors = set()
107 targetancestors = set()
108
108
109 editor = None
109 editor = None
110 if opts.get('edit'):
110 if opts.get('edit'):
111 editor = cmdutil.commitforceeditor
111 editor = cmdutil.commitforceeditor
112
112
113 lock = wlock = None
113 lock = wlock = None
114 try:
114 try:
115 lock = repo.lock()
115 lock = repo.lock()
116 wlock = repo.wlock()
116 wlock = repo.wlock()
117
117
118 # Validate input and define rebasing points
118 # Validate input and define rebasing points
119 destf = opts.get('dest', None)
119 destf = opts.get('dest', None)
120 srcf = opts.get('source', None)
120 srcf = opts.get('source', None)
121 basef = opts.get('base', None)
121 basef = opts.get('base', None)
122 contf = opts.get('continue')
122 contf = opts.get('continue')
123 abortf = opts.get('abort')
123 abortf = opts.get('abort')
124 collapsef = opts.get('collapse', False)
124 collapsef = opts.get('collapse', False)
125 collapsemsg = cmdutil.logmessage(ui, opts)
125 collapsemsg = cmdutil.logmessage(ui, opts)
126 extrafn = opts.get('extrafn') # internal, used by e.g. hgsubversion
126 extrafn = opts.get('extrafn') # internal, used by e.g. hgsubversion
127 keepf = opts.get('keep', False)
127 keepf = opts.get('keep', False)
128 keepbranchesf = opts.get('keepbranches', False)
128 keepbranchesf = opts.get('keepbranches', False)
129 detachf = opts.get('detach', False)
129 detachf = opts.get('detach', False)
130 # keepopen is not meant for use on the command line, but by
130 # keepopen is not meant for use on the command line, but by
131 # other extensions
131 # other extensions
132 keepopen = opts.get('keepopen', False)
132 keepopen = opts.get('keepopen', False)
133
133
134 if collapsemsg and not collapsef:
134 if collapsemsg and not collapsef:
135 raise util.Abort(
135 raise util.Abort(
136 _('message can only be specified with collapse'))
136 _('message can only be specified with collapse'))
137
137
138 if contf or abortf:
138 if contf or abortf:
139 if contf and abortf:
139 if contf and abortf:
140 raise util.Abort(_('cannot use both abort and continue'))
140 raise util.Abort(_('cannot use both abort and continue'))
141 if collapsef:
141 if collapsef:
142 raise util.Abort(
142 raise util.Abort(
143 _('cannot use collapse with continue or abort'))
143 _('cannot use collapse with continue or abort'))
144 if detachf:
144 if detachf:
145 raise util.Abort(_('cannot use detach with continue or abort'))
145 raise util.Abort(_('cannot use detach 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 'revision and a base'))
159 'revision and a base'))
160 if detachf:
160 if detachf:
161 if not srcf:
161 if not srcf:
162 raise util.Abort(
162 raise util.Abort(
163 _('detach requires a revision to be specified'))
163 _('detach requires a revision to be specified'))
164 if basef:
164 if basef:
165 raise util.Abort(_('cannot specify a base with detach'))
165 raise util.Abort(_('cannot specify a base with detach'))
166
166
167 cmdutil.bailifchanged(repo)
167 cmdutil.bailifchanged(repo)
168 result = buildstate(repo, destf, srcf, basef, detachf)
168
169 if not destf:
170 # Destination defaults to the latest revision in the current branch
171 branch = repo[None].branch()
172 dest = repo[branch]
173 else:
174 dest = repo[destf]
175
176 if srcf:
177 revsetargs = ('(%s)::', srcf)
178 else:
179 base = basef or '.'
180 revsetargs = ('(children(ancestor(%s, %d)) and ::(%s))::',
181 base, dest, base)
182
183 rebaseset = [c.rev() for c in repo.set(*revsetargs)]
184 if rebaseset:
185 result = buildstate(repo, dest, rebaseset, detachf)
186 else:
187 repo.ui.debug(_('base is ancestor of destination'))
188 result = None
169 if not result:
189 if not result:
170 # Empty state built, nothing to rebase
190 # Empty state built, nothing to rebase
171 ui.status(_('nothing to rebase\n'))
191 ui.status(_('nothing to rebase\n'))
172 return 1
192 return 1
173 else:
193 else:
174 originalwd, target, state = result
194 originalwd, target, state = result
175 if collapsef:
195 if collapsef:
176 targetancestors = set(repo.changelog.ancestors(target))
196 targetancestors = set(repo.changelog.ancestors(target))
177 external = checkexternal(repo, state, targetancestors)
197 external = checkexternal(repo, state, targetancestors)
178
198
179 if keepbranchesf:
199 if keepbranchesf:
180 assert not extrafn, 'cannot use both keepbranches and extrafn'
200 assert not extrafn, 'cannot use both keepbranches and extrafn'
181 def extrafn(ctx, extra):
201 def extrafn(ctx, extra):
182 extra['branch'] = ctx.branch()
202 extra['branch'] = ctx.branch()
183 if collapsef:
203 if collapsef:
184 branches = set()
204 branches = set()
185 for rev in state:
205 for rev in state:
186 branches.add(repo[rev].branch())
206 branches.add(repo[rev].branch())
187 if len(branches) > 1:
207 if len(branches) > 1:
188 raise util.Abort(_('cannot collapse multiple named '
208 raise util.Abort(_('cannot collapse multiple named '
189 'branches'))
209 'branches'))
190
210
191
211
192 # Rebase
212 # Rebase
193 if not targetancestors:
213 if not targetancestors:
194 targetancestors = set(repo.changelog.ancestors(target))
214 targetancestors = set(repo.changelog.ancestors(target))
195 targetancestors.add(target)
215 targetancestors.add(target)
196
216
197 # Keep track of the current bookmarks in order to reset them later
217 # Keep track of the current bookmarks in order to reset them later
198 currentbookmarks = repo._bookmarks.copy()
218 currentbookmarks = repo._bookmarks.copy()
199
219
200 sortedstate = sorted(state)
220 sortedstate = sorted(state)
201 total = len(sortedstate)
221 total = len(sortedstate)
202 pos = 0
222 pos = 0
203 for rev in sortedstate:
223 for rev in sortedstate:
204 pos += 1
224 pos += 1
205 if state[rev] == -1:
225 if state[rev] == -1:
206 ui.progress(_("rebasing"), pos, ("%d:%s" % (rev, repo[rev])),
226 ui.progress(_("rebasing"), pos, ("%d:%s" % (rev, repo[rev])),
207 _('changesets'), total)
227 _('changesets'), total)
208 storestatus(repo, originalwd, target, state, collapsef, keepf,
228 storestatus(repo, originalwd, target, state, collapsef, keepf,
209 keepbranchesf, external)
229 keepbranchesf, external)
210 p1, p2 = defineparents(repo, rev, target, state,
230 p1, p2 = defineparents(repo, rev, target, state,
211 targetancestors)
231 targetancestors)
212 if len(repo.parents()) == 2:
232 if len(repo.parents()) == 2:
213 repo.ui.debug('resuming interrupted rebase\n')
233 repo.ui.debug('resuming interrupted rebase\n')
214 else:
234 else:
215 try:
235 try:
216 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''))
236 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''))
217 stats = rebasenode(repo, rev, p1, state)
237 stats = rebasenode(repo, rev, p1, state)
218 if stats and stats[3] > 0:
238 if stats and stats[3] > 0:
219 raise util.Abort(_('unresolved conflicts (see hg '
239 raise util.Abort(_('unresolved conflicts (see hg '
220 'resolve, then hg rebase --continue)'))
240 'resolve, then hg rebase --continue)'))
221 finally:
241 finally:
222 ui.setconfig('ui', 'forcemerge', '')
242 ui.setconfig('ui', 'forcemerge', '')
223 cmdutil.duplicatecopies(repo, rev, target, p2)
243 cmdutil.duplicatecopies(repo, rev, target, p2)
224 if not collapsef:
244 if not collapsef:
225 newrev = concludenode(repo, rev, p1, p2, extrafn=extrafn,
245 newrev = concludenode(repo, rev, p1, p2, extrafn=extrafn,
226 editor=editor)
246 editor=editor)
227 else:
247 else:
228 # Skip commit if we are collapsing
248 # Skip commit if we are collapsing
229 repo.dirstate.setparents(repo[p1].node())
249 repo.dirstate.setparents(repo[p1].node())
230 newrev = None
250 newrev = None
231 # Update the state
251 # Update the state
232 if newrev is not None:
252 if newrev is not None:
233 state[rev] = repo[newrev].rev()
253 state[rev] = repo[newrev].rev()
234 else:
254 else:
235 if not collapsef:
255 if not collapsef:
236 ui.note(_('no changes, revision %d skipped\n') % rev)
256 ui.note(_('no changes, revision %d skipped\n') % rev)
237 ui.debug('next revision set to %s\n' % p1)
257 ui.debug('next revision set to %s\n' % p1)
238 skipped.add(rev)
258 skipped.add(rev)
239 state[rev] = p1
259 state[rev] = p1
240
260
241 ui.progress(_('rebasing'), None)
261 ui.progress(_('rebasing'), None)
242 ui.note(_('rebase merging completed\n'))
262 ui.note(_('rebase merging completed\n'))
243
263
244 if collapsef and not keepopen:
264 if collapsef and not keepopen:
245 p1, p2 = defineparents(repo, min(state), target,
265 p1, p2 = defineparents(repo, min(state), target,
246 state, targetancestors)
266 state, targetancestors)
247 if collapsemsg:
267 if collapsemsg:
248 commitmsg = collapsemsg
268 commitmsg = collapsemsg
249 else:
269 else:
250 commitmsg = 'Collapsed revision'
270 commitmsg = 'Collapsed revision'
251 for rebased in state:
271 for rebased in state:
252 if rebased not in skipped and state[rebased] != nullmerge:
272 if rebased not in skipped and state[rebased] != nullmerge:
253 commitmsg += '\n* %s' % repo[rebased].description()
273 commitmsg += '\n* %s' % repo[rebased].description()
254 commitmsg = ui.edit(commitmsg, repo.ui.username())
274 commitmsg = ui.edit(commitmsg, repo.ui.username())
255 newrev = concludenode(repo, rev, p1, external, commitmsg=commitmsg,
275 newrev = concludenode(repo, rev, p1, external, commitmsg=commitmsg,
256 extrafn=extrafn, editor=editor)
276 extrafn=extrafn, editor=editor)
257
277
258 if 'qtip' in repo.tags():
278 if 'qtip' in repo.tags():
259 updatemq(repo, state, skipped, **opts)
279 updatemq(repo, state, skipped, **opts)
260
280
261 if currentbookmarks:
281 if currentbookmarks:
262 # Nodeids are needed to reset bookmarks
282 # Nodeids are needed to reset bookmarks
263 nstate = {}
283 nstate = {}
264 for k, v in state.iteritems():
284 for k, v in state.iteritems():
265 if v != nullmerge:
285 if v != nullmerge:
266 nstate[repo[k].node()] = repo[v].node()
286 nstate[repo[k].node()] = repo[v].node()
267
287
268 if not keepf:
288 if not keepf:
269 # Remove no more useful revisions
289 # Remove no more useful revisions
270 rebased = [rev for rev in state if state[rev] != nullmerge]
290 rebased = [rev for rev in state if state[rev] != nullmerge]
271 if rebased:
291 if rebased:
272 if set(repo.changelog.descendants(min(rebased))) - set(state):
292 if set(repo.changelog.descendants(min(rebased))) - set(state):
273 ui.warn(_("warning: new changesets detected "
293 ui.warn(_("warning: new changesets detected "
274 "on source branch, not stripping\n"))
294 "on source branch, not stripping\n"))
275 else:
295 else:
276 # backup the old csets by default
296 # backup the old csets by default
277 repair.strip(ui, repo, repo[min(rebased)].node(), "all")
297 repair.strip(ui, repo, repo[min(rebased)].node(), "all")
278
298
279 if currentbookmarks:
299 if currentbookmarks:
280 updatebookmarks(repo, nstate, currentbookmarks, **opts)
300 updatebookmarks(repo, nstate, currentbookmarks, **opts)
281
301
282 clearstatus(repo)
302 clearstatus(repo)
283 ui.note(_("rebase completed\n"))
303 ui.note(_("rebase completed\n"))
284 if os.path.exists(repo.sjoin('undo')):
304 if os.path.exists(repo.sjoin('undo')):
285 util.unlinkpath(repo.sjoin('undo'))
305 util.unlinkpath(repo.sjoin('undo'))
286 if skipped:
306 if skipped:
287 ui.note(_("%d revisions have been skipped\n") % len(skipped))
307 ui.note(_("%d revisions have been skipped\n") % len(skipped))
288 finally:
308 finally:
289 release(lock, wlock)
309 release(lock, wlock)
290
310
291 def checkexternal(repo, state, targetancestors):
311 def checkexternal(repo, state, targetancestors):
292 """Check whether one or more external revisions need to be taken in
312 """Check whether one or more external revisions need to be taken in
293 consideration. In the latter case, abort.
313 consideration. In the latter case, abort.
294 """
314 """
295 external = nullrev
315 external = nullrev
296 source = min(state)
316 source = min(state)
297 for rev in state:
317 for rev in state:
298 if rev == source:
318 if rev == source:
299 continue
319 continue
300 # Check externals and fail if there are more than one
320 # Check externals and fail if there are more than one
301 for p in repo[rev].parents():
321 for p in repo[rev].parents():
302 if (p.rev() not in state
322 if (p.rev() not in state
303 and p.rev() not in targetancestors):
323 and p.rev() not in targetancestors):
304 if external != nullrev:
324 if external != nullrev:
305 raise util.Abort(_('unable to collapse, there is more '
325 raise util.Abort(_('unable to collapse, there is more '
306 'than one external parent'))
326 'than one external parent'))
307 external = p.rev()
327 external = p.rev()
308 return external
328 return external
309
329
310 def concludenode(repo, rev, p1, p2, commitmsg=None, editor=None, extrafn=None):
330 def concludenode(repo, rev, p1, p2, commitmsg=None, editor=None, extrafn=None):
311 'Commit the changes and store useful information in extra'
331 'Commit the changes and store useful information in extra'
312 try:
332 try:
313 repo.dirstate.setparents(repo[p1].node(), repo[p2].node())
333 repo.dirstate.setparents(repo[p1].node(), repo[p2].node())
314 ctx = repo[rev]
334 ctx = repo[rev]
315 if commitmsg is None:
335 if commitmsg is None:
316 commitmsg = ctx.description()
336 commitmsg = ctx.description()
317 extra = {'rebase_source': ctx.hex()}
337 extra = {'rebase_source': ctx.hex()}
318 if extrafn:
338 if extrafn:
319 extrafn(ctx, extra)
339 extrafn(ctx, extra)
320 # Commit might fail if unresolved files exist
340 # Commit might fail if unresolved files exist
321 newrev = repo.commit(text=commitmsg, user=ctx.user(),
341 newrev = repo.commit(text=commitmsg, user=ctx.user(),
322 date=ctx.date(), extra=extra, editor=editor)
342 date=ctx.date(), extra=extra, editor=editor)
323 repo.dirstate.setbranch(repo[newrev].branch())
343 repo.dirstate.setbranch(repo[newrev].branch())
324 return newrev
344 return newrev
325 except util.Abort:
345 except util.Abort:
326 # Invalidate the previous setparents
346 # Invalidate the previous setparents
327 repo.dirstate.invalidate()
347 repo.dirstate.invalidate()
328 raise
348 raise
329
349
330 def rebasenode(repo, rev, p1, state):
350 def rebasenode(repo, rev, p1, state):
331 'Rebase a single revision'
351 'Rebase a single revision'
332 # Merge phase
352 # Merge phase
333 # Update to target and merge it with local
353 # Update to target and merge it with local
334 if repo['.'].rev() != repo[p1].rev():
354 if repo['.'].rev() != repo[p1].rev():
335 repo.ui.debug(" update to %d:%s\n" % (repo[p1].rev(), repo[p1]))
355 repo.ui.debug(" update to %d:%s\n" % (repo[p1].rev(), repo[p1]))
336 merge.update(repo, p1, False, True, False)
356 merge.update(repo, p1, False, True, False)
337 else:
357 else:
338 repo.ui.debug(" already in target\n")
358 repo.ui.debug(" already in target\n")
339 repo.dirstate.write()
359 repo.dirstate.write()
340 repo.ui.debug(" merge against %d:%s\n" % (repo[rev].rev(), repo[rev]))
360 repo.ui.debug(" merge against %d:%s\n" % (repo[rev].rev(), repo[rev]))
341 base = None
361 base = None
342 if repo[rev].rev() != repo[min(state)].rev():
362 if repo[rev].rev() != repo[min(state)].rev():
343 base = repo[rev].p1().node()
363 base = repo[rev].p1().node()
344 return merge.update(repo, rev, True, True, False, base)
364 return merge.update(repo, rev, True, True, False, base)
345
365
346 def defineparents(repo, rev, target, state, targetancestors):
366 def defineparents(repo, rev, target, state, targetancestors):
347 'Return the new parent relationship of the revision that will be rebased'
367 'Return the new parent relationship of the revision that will be rebased'
348 parents = repo[rev].parents()
368 parents = repo[rev].parents()
349 p1 = p2 = nullrev
369 p1 = p2 = nullrev
350
370
351 P1n = parents[0].rev()
371 P1n = parents[0].rev()
352 if P1n in targetancestors:
372 if P1n in targetancestors:
353 p1 = target
373 p1 = target
354 elif P1n in state:
374 elif P1n in state:
355 if state[P1n] == nullmerge:
375 if state[P1n] == nullmerge:
356 p1 = target
376 p1 = target
357 else:
377 else:
358 p1 = state[P1n]
378 p1 = state[P1n]
359 else: # P1n external
379 else: # P1n external
360 p1 = target
380 p1 = target
361 p2 = P1n
381 p2 = P1n
362
382
363 if len(parents) == 2 and parents[1].rev() not in targetancestors:
383 if len(parents) == 2 and parents[1].rev() not in targetancestors:
364 P2n = parents[1].rev()
384 P2n = parents[1].rev()
365 # interesting second parent
385 # interesting second parent
366 if P2n in state:
386 if P2n in state:
367 if p1 == target: # P1n in targetancestors or external
387 if p1 == target: # P1n in targetancestors or external
368 p1 = state[P2n]
388 p1 = state[P2n]
369 else:
389 else:
370 p2 = state[P2n]
390 p2 = state[P2n]
371 else: # P2n external
391 else: # P2n external
372 if p2 != nullrev: # P1n external too => rev is a merged revision
392 if p2 != nullrev: # P1n external too => rev is a merged revision
373 raise util.Abort(_('cannot use revision %d as base, result '
393 raise util.Abort(_('cannot use revision %d as base, result '
374 'would have 3 parents') % rev)
394 'would have 3 parents') % rev)
375 p2 = P2n
395 p2 = P2n
376 repo.ui.debug(" future parents are %d and %d\n" %
396 repo.ui.debug(" future parents are %d and %d\n" %
377 (repo[p1].rev(), repo[p2].rev()))
397 (repo[p1].rev(), repo[p2].rev()))
378 return p1, p2
398 return p1, p2
379
399
380 def isagitpatch(repo, patchname):
400 def isagitpatch(repo, patchname):
381 'Return true if the given patch is in git format'
401 'Return true if the given patch is in git format'
382 mqpatch = os.path.join(repo.mq.path, patchname)
402 mqpatch = os.path.join(repo.mq.path, patchname)
383 for line in patch.linereader(file(mqpatch, 'rb')):
403 for line in patch.linereader(file(mqpatch, 'rb')):
384 if line.startswith('diff --git'):
404 if line.startswith('diff --git'):
385 return True
405 return True
386 return False
406 return False
387
407
388 def updatemq(repo, state, skipped, **opts):
408 def updatemq(repo, state, skipped, **opts):
389 'Update rebased mq patches - finalize and then import them'
409 'Update rebased mq patches - finalize and then import them'
390 mqrebase = {}
410 mqrebase = {}
391 mq = repo.mq
411 mq = repo.mq
392 original_series = mq.fullseries[:]
412 original_series = mq.fullseries[:]
393
413
394 for p in mq.applied:
414 for p in mq.applied:
395 rev = repo[p.node].rev()
415 rev = repo[p.node].rev()
396 if rev in state:
416 if rev in state:
397 repo.ui.debug('revision %d is an mq patch (%s), finalize it.\n' %
417 repo.ui.debug('revision %d is an mq patch (%s), finalize it.\n' %
398 (rev, p.name))
418 (rev, p.name))
399 mqrebase[rev] = (p.name, isagitpatch(repo, p.name))
419 mqrebase[rev] = (p.name, isagitpatch(repo, p.name))
400
420
401 if mqrebase:
421 if mqrebase:
402 mq.finish(repo, mqrebase.keys())
422 mq.finish(repo, mqrebase.keys())
403
423
404 # We must start import from the newest revision
424 # We must start import from the newest revision
405 for rev in sorted(mqrebase, reverse=True):
425 for rev in sorted(mqrebase, reverse=True):
406 if rev not in skipped:
426 if rev not in skipped:
407 name, isgit = mqrebase[rev]
427 name, isgit = mqrebase[rev]
408 repo.ui.debug('import mq patch %d (%s)\n' % (state[rev], name))
428 repo.ui.debug('import mq patch %d (%s)\n' % (state[rev], name))
409 mq.qimport(repo, (), patchname=name, git=isgit,
429 mq.qimport(repo, (), patchname=name, git=isgit,
410 rev=[str(state[rev])])
430 rev=[str(state[rev])])
411
431
412 # restore old series to preserve guards
432 # restore old series to preserve guards
413 mq.fullseries = original_series
433 mq.fullseries = original_series
414 mq.series_dirty = True
434 mq.series_dirty = True
415 mq.savedirty()
435 mq.savedirty()
416
436
417 def updatebookmarks(repo, nstate, originalbookmarks, **opts):
437 def updatebookmarks(repo, nstate, originalbookmarks, **opts):
418 'Move bookmarks to their correct changesets'
438 'Move bookmarks to their correct changesets'
419 current = repo._bookmarkcurrent
439 current = repo._bookmarkcurrent
420 for k, v in originalbookmarks.iteritems():
440 for k, v in originalbookmarks.iteritems():
421 if v in nstate:
441 if v in nstate:
422 if nstate[v] != nullmerge:
442 if nstate[v] != nullmerge:
423 # reset the pointer if the bookmark was moved incorrectly
443 # reset the pointer if the bookmark was moved incorrectly
424 if k != current:
444 if k != current:
425 repo._bookmarks[k] = nstate[v]
445 repo._bookmarks[k] = nstate[v]
426
446
427 bookmarks.write(repo)
447 bookmarks.write(repo)
428
448
429 def storestatus(repo, originalwd, target, state, collapse, keep, keepbranches,
449 def storestatus(repo, originalwd, target, state, collapse, keep, keepbranches,
430 external):
450 external):
431 'Store the current status to allow recovery'
451 'Store the current status to allow recovery'
432 f = repo.opener("rebasestate", "w")
452 f = repo.opener("rebasestate", "w")
433 f.write(repo[originalwd].hex() + '\n')
453 f.write(repo[originalwd].hex() + '\n')
434 f.write(repo[target].hex() + '\n')
454 f.write(repo[target].hex() + '\n')
435 f.write(repo[external].hex() + '\n')
455 f.write(repo[external].hex() + '\n')
436 f.write('%d\n' % int(collapse))
456 f.write('%d\n' % int(collapse))
437 f.write('%d\n' % int(keep))
457 f.write('%d\n' % int(keep))
438 f.write('%d\n' % int(keepbranches))
458 f.write('%d\n' % int(keepbranches))
439 for d, v in state.iteritems():
459 for d, v in state.iteritems():
440 oldrev = repo[d].hex()
460 oldrev = repo[d].hex()
441 newrev = repo[v].hex()
461 newrev = repo[v].hex()
442 f.write("%s:%s\n" % (oldrev, newrev))
462 f.write("%s:%s\n" % (oldrev, newrev))
443 f.close()
463 f.close()
444 repo.ui.debug('rebase status stored\n')
464 repo.ui.debug('rebase status stored\n')
445
465
446 def clearstatus(repo):
466 def clearstatus(repo):
447 'Remove the status files'
467 'Remove the status files'
448 if os.path.exists(repo.join("rebasestate")):
468 if os.path.exists(repo.join("rebasestate")):
449 util.unlinkpath(repo.join("rebasestate"))
469 util.unlinkpath(repo.join("rebasestate"))
450
470
451 def restorestatus(repo):
471 def restorestatus(repo):
452 'Restore a previously stored status'
472 'Restore a previously stored status'
453 try:
473 try:
454 target = None
474 target = None
455 collapse = False
475 collapse = False
456 external = nullrev
476 external = nullrev
457 state = {}
477 state = {}
458 f = repo.opener("rebasestate")
478 f = repo.opener("rebasestate")
459 for i, l in enumerate(f.read().splitlines()):
479 for i, l in enumerate(f.read().splitlines()):
460 if i == 0:
480 if i == 0:
461 originalwd = repo[l].rev()
481 originalwd = repo[l].rev()
462 elif i == 1:
482 elif i == 1:
463 target = repo[l].rev()
483 target = repo[l].rev()
464 elif i == 2:
484 elif i == 2:
465 external = repo[l].rev()
485 external = repo[l].rev()
466 elif i == 3:
486 elif i == 3:
467 collapse = bool(int(l))
487 collapse = bool(int(l))
468 elif i == 4:
488 elif i == 4:
469 keep = bool(int(l))
489 keep = bool(int(l))
470 elif i == 5:
490 elif i == 5:
471 keepbranches = bool(int(l))
491 keepbranches = bool(int(l))
472 else:
492 else:
473 oldrev, newrev = l.split(':')
493 oldrev, newrev = l.split(':')
474 state[repo[oldrev].rev()] = repo[newrev].rev()
494 state[repo[oldrev].rev()] = repo[newrev].rev()
475 skipped = set()
495 skipped = set()
476 # recompute the set of skipped revs
496 # recompute the set of skipped revs
477 if not collapse:
497 if not collapse:
478 seen = set([target])
498 seen = set([target])
479 for old, new in sorted(state.items()):
499 for old, new in sorted(state.items()):
480 if new != nullrev and new in seen:
500 if new != nullrev and new in seen:
481 skipped.add(old)
501 skipped.add(old)
482 seen.add(new)
502 seen.add(new)
483 repo.ui.debug('computed skipped revs: %s\n' % skipped)
503 repo.ui.debug('computed skipped revs: %s\n' % skipped)
484 repo.ui.debug('rebase status resumed\n')
504 repo.ui.debug('rebase status resumed\n')
485 return (originalwd, target, state, skipped,
505 return (originalwd, target, state, skipped,
486 collapse, keep, keepbranches, external)
506 collapse, keep, keepbranches, external)
487 except IOError, err:
507 except IOError, err:
488 if err.errno != errno.ENOENT:
508 if err.errno != errno.ENOENT:
489 raise
509 raise
490 raise util.Abort(_('no rebase in progress'))
510 raise util.Abort(_('no rebase in progress'))
491
511
492 def abort(repo, originalwd, target, state):
512 def abort(repo, originalwd, target, state):
493 'Restore the repository to its original state'
513 'Restore the repository to its original state'
494 if set(repo.changelog.descendants(target)) - set(state.values()):
514 if set(repo.changelog.descendants(target)) - set(state.values()):
495 repo.ui.warn(_("warning: new changesets detected on target branch, "
515 repo.ui.warn(_("warning: new changesets detected on target branch, "
496 "can't abort\n"))
516 "can't abort\n"))
497 return -1
517 return -1
498 else:
518 else:
499 # Strip from the first rebased revision
519 # Strip from the first rebased revision
500 merge.update(repo, repo[originalwd].rev(), False, True, False)
520 merge.update(repo, repo[originalwd].rev(), False, True, False)
501 rebased = filter(lambda x: x > -1 and x != target, state.values())
521 rebased = filter(lambda x: x > -1 and x != target, state.values())
502 if rebased:
522 if rebased:
503 strippoint = min(rebased)
523 strippoint = min(rebased)
504 # no backup of rebased cset versions needed
524 # no backup of rebased cset versions needed
505 repair.strip(repo.ui, repo, repo[strippoint].node())
525 repair.strip(repo.ui, repo, repo[strippoint].node())
506 clearstatus(repo)
526 clearstatus(repo)
507 repo.ui.warn(_('rebase aborted\n'))
527 repo.ui.warn(_('rebase aborted\n'))
508 return 0
528 return 0
509
529
510 def buildstate(repo, dest, src, base, detach):
530 def buildstate(repo, dest, rebaseset, detach):
511 'Define which revisions are going to be rebased and where'
531 '''Define which revisions are going to be rebased and where
512 targetancestors = set()
513 detachset = set()
514
532
515 if not dest:
533 repo: repo
516 # Destination defaults to the latest revision in the current branch
534 dest: context
517 branch = repo[None].branch()
535 rebaseset: set of rev
518 dest = repo[branch].rev()
536 detach: boolean'''
519 else:
520 dest = repo[dest].rev()
521
537
522 # This check isn't strictly necessary, since mq detects commits over an
538 # This check isn't strictly necessary, since mq detects commits over an
523 # applied patch. But it prevents messing up the working directory when
539 # applied patch. But it prevents messing up the working directory when
524 # a partially completed rebase is blocked by mq.
540 # a partially completed rebase is blocked by mq.
525 if 'qtip' in repo.tags() and (repo[dest].node() in
541 if 'qtip' in repo.tags() and (dest.node() in
526 [s.node for s in repo.mq.applied]):
542 [s.node for s in repo.mq.applied]):
527 raise util.Abort(_('cannot rebase onto an applied mq patch'))
543 raise util.Abort(_('cannot rebase onto an applied mq patch'))
528
544
529 if src:
545 detachset = set()
530 commonbase = repo[src].ancestor(repo[dest])
546 roots = list(repo.set('roots(%ld)', rebaseset))
531 if commonbase == repo[src]:
547 if not roots:
532 raise util.Abort(_('source is ancestor of destination'))
548 raise util.Abort( _('no matching revisions'))
533 if commonbase == repo[dest]:
549 if len(roots) > 1:
534 samebranch = repo[src].branch() == repo[dest].branch()
550 raise util.Abort( _("can't rebase multiple roots"))
535 if samebranch and repo[src] in repo[dest].children():
551 root = roots[0]
536 raise util.Abort(_('source is a child of destination'))
537 # rebase on ancestor, force detach
538 detach = True
539 source = repo[src].rev()
540 if detach:
541 # We need to keep track of source's ancestors up to the common base
542 srcancestors = set(repo.changelog.ancestors(source))
543 baseancestors = set(repo.changelog.ancestors(commonbase.rev()))
544 detachset = srcancestors - baseancestors
545 detachset.discard(commonbase.rev())
546 else:
547 if base:
548 cwd = repo[base].rev()
549 else:
550 cwd = repo['.'].rev()
551
552
552 if cwd == dest:
553 commonbase = root.ancestor(dest)
553 repo.ui.debug('source and destination are the same\n')
554 if commonbase == root:
554 return None
555 raise util.Abort(_('source is ancestor of destination'))
555
556 if commonbase == dest:
556 targetancestors = set(repo.changelog.ancestors(dest))
557 samebranch = root.branch() == dest.branch()
557 if cwd in targetancestors:
558 if samebranch and root in dest.children():
558 repo.ui.debug('source is ancestor of destination\n')
559 repo.ui.debug(_('source is a child of destination'))
559 return None
560 return None
561 # rebase on ancestor, force detach
562 detach = True
563 if detach:
564 detachset = [c.rev() for c in repo.set('::%d - ::%d - %d',
565 root, commonbase, root)]
560
566
561 cwdancestors = set(repo.changelog.ancestors(cwd))
567 repo.ui.debug('rebase onto %d starting from %d\n' % (dest, root))
562 if dest in cwdancestors:
568 state = dict.fromkeys(rebaseset, nullrev)
563 repo.ui.debug('source is descendant of destination\n')
564 return None
565
566 cwdancestors.add(cwd)
567 rebasingbranch = cwdancestors - targetancestors
568 source = min(rebasingbranch)
569
570 repo.ui.debug('rebase onto %d starting from %d\n' % (dest, source))
571 state = dict.fromkeys(repo.changelog.descendants(source), nullrev)
572 state.update(dict.fromkeys(detachset, nullmerge))
569 state.update(dict.fromkeys(detachset, nullmerge))
573 state[source] = nullrev
570 return repo['.'].rev(), dest.rev(), state
574 return repo['.'].rev(), repo[dest].rev(), state
575
571
576 def pullrebase(orig, ui, repo, *args, **opts):
572 def pullrebase(orig, ui, repo, *args, **opts):
577 'Call rebase after pull if the latter has been invoked with --rebase'
573 'Call rebase after pull if the latter has been invoked with --rebase'
578 if opts.get('rebase'):
574 if opts.get('rebase'):
579 if opts.get('update'):
575 if opts.get('update'):
580 del opts['update']
576 del opts['update']
581 ui.debug('--update and --rebase are not compatible, ignoring '
577 ui.debug('--update and --rebase are not compatible, ignoring '
582 'the update flag\n')
578 'the update flag\n')
583
579
584 cmdutil.bailifchanged(repo)
580 cmdutil.bailifchanged(repo)
585 revsprepull = len(repo)
581 revsprepull = len(repo)
586 origpostincoming = commands.postincoming
582 origpostincoming = commands.postincoming
587 def _dummy(*args, **kwargs):
583 def _dummy(*args, **kwargs):
588 pass
584 pass
589 commands.postincoming = _dummy
585 commands.postincoming = _dummy
590 try:
586 try:
591 orig(ui, repo, *args, **opts)
587 orig(ui, repo, *args, **opts)
592 finally:
588 finally:
593 commands.postincoming = origpostincoming
589 commands.postincoming = origpostincoming
594 revspostpull = len(repo)
590 revspostpull = len(repo)
595 if revspostpull > revsprepull:
591 if revspostpull > revsprepull:
596 rebase(ui, repo, **opts)
592 rebase(ui, repo, **opts)
597 branch = repo[None].branch()
593 branch = repo[None].branch()
598 dest = repo[branch].rev()
594 dest = repo[branch].rev()
599 if dest != repo['.'].rev():
595 if dest != repo['.'].rev():
600 # there was nothing to rebase we force an update
596 # there was nothing to rebase we force an update
601 hg.update(repo, dest)
597 hg.update(repo, dest)
602 else:
598 else:
603 if opts.get('tool'):
599 if opts.get('tool'):
604 raise util.Abort(_('--tool can only be used with --rebase'))
600 raise util.Abort(_('--tool can only be used with --rebase'))
605 orig(ui, repo, *args, **opts)
601 orig(ui, repo, *args, **opts)
606
602
607 def uisetup(ui):
603 def uisetup(ui):
608 'Replace pull with a decorator to provide --rebase option'
604 'Replace pull with a decorator to provide --rebase option'
609 entry = extensions.wrapcommand(commands.table, 'pull', pullrebase)
605 entry = extensions.wrapcommand(commands.table, 'pull', pullrebase)
610 entry[1].append(('', 'rebase', None,
606 entry[1].append(('', 'rebase', None,
611 _("rebase working directory to branch head")))
607 _("rebase working directory to branch head")))
612 entry[1].append(('t', 'tool', '',
608 entry[1].append(('t', 'tool', '',
613 _("specify merge tool for rebase")))
609 _("specify merge tool for rebase")))
@@ -1,482 +1,482
1 $ cat >> $HGRCPATH <<EOF
1 $ cat >> $HGRCPATH <<EOF
2 > [extensions]
2 > [extensions]
3 > graphlog=
3 > graphlog=
4 > rebase=
4 > rebase=
5 >
5 >
6 > [alias]
6 > [alias]
7 > tglog = log -G --template "{rev}: '{desc}' {branches}\n"
7 > tglog = log -G --template "{rev}: '{desc}' {branches}\n"
8 > EOF
8 > EOF
9
9
10 Create repo a:
10 Create repo a:
11
11
12 $ hg init a
12 $ hg init a
13 $ cd a
13 $ cd a
14 $ hg unbundle $TESTDIR/bundles/rebase.hg
14 $ hg unbundle $TESTDIR/bundles/rebase.hg
15 adding changesets
15 adding changesets
16 adding manifests
16 adding manifests
17 adding file changes
17 adding file changes
18 added 8 changesets with 7 changes to 7 files (+2 heads)
18 added 8 changesets with 7 changes to 7 files (+2 heads)
19 (run 'hg heads' to see heads, 'hg merge' to merge)
19 (run 'hg heads' to see heads, 'hg merge' to merge)
20 $ hg up tip
20 $ hg up tip
21 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
21 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
22
22
23 $ hg tglog
23 $ hg tglog
24 @ 7: 'H'
24 @ 7: 'H'
25 |
25 |
26 | o 6: 'G'
26 | o 6: 'G'
27 |/|
27 |/|
28 o | 5: 'F'
28 o | 5: 'F'
29 | |
29 | |
30 | o 4: 'E'
30 | o 4: 'E'
31 |/
31 |/
32 | o 3: 'D'
32 | o 3: 'D'
33 | |
33 | |
34 | o 2: 'C'
34 | o 2: 'C'
35 | |
35 | |
36 | o 1: 'B'
36 | o 1: 'B'
37 |/
37 |/
38 o 0: 'A'
38 o 0: 'A'
39
39
40 $ cd ..
40 $ cd ..
41
41
42
42
43 Rebasing B onto H:
43 Rebasing B onto H:
44
44
45 $ hg clone -q -u 3 a a1
45 $ hg clone -q -u 3 a a1
46 $ cd a1
46 $ cd a1
47
47
48 $ hg rebase --collapse --keepbranches
48 $ hg rebase --collapse --keepbranches
49 saved backup bundle to $TESTTMP/a1/.hg/strip-backup/*-backup.hg (glob)
49 saved backup bundle to $TESTTMP/a1/.hg/strip-backup/*-backup.hg (glob)
50
50
51 $ hg tglog
51 $ hg tglog
52 @ 5: 'Collapsed revision
52 @ 5: 'Collapsed revision
53 | * B
53 | * B
54 | * C
54 | * C
55 | * D'
55 | * D'
56 o 4: 'H'
56 o 4: 'H'
57 |
57 |
58 | o 3: 'G'
58 | o 3: 'G'
59 |/|
59 |/|
60 o | 2: 'F'
60 o | 2: 'F'
61 | |
61 | |
62 | o 1: 'E'
62 | o 1: 'E'
63 |/
63 |/
64 o 0: 'A'
64 o 0: 'A'
65
65
66 $ hg manifest
66 $ hg manifest
67 A
67 A
68 B
68 B
69 C
69 C
70 D
70 D
71 F
71 F
72 H
72 H
73
73
74 $ cd ..
74 $ cd ..
75
75
76
76
77 Rebasing G onto H:
77 Rebasing E onto H:
78
78
79 $ hg clone -q -u . a a2
79 $ hg clone -q -u . a a2
80 $ cd a2
80 $ cd a2
81
81
82 $ hg rebase --base 6 --collapse
82 $ hg rebase --source 4 --collapse
83 saved backup bundle to $TESTTMP/a2/.hg/strip-backup/*-backup.hg (glob)
83 saved backup bundle to $TESTTMP/a2/.hg/strip-backup/*-backup.hg (glob)
84
84
85 $ hg tglog
85 $ hg tglog
86 @ 6: 'Collapsed revision
86 @ 6: 'Collapsed revision
87 | * E
87 | * E
88 | * G'
88 | * G'
89 o 5: 'H'
89 o 5: 'H'
90 |
90 |
91 o 4: 'F'
91 o 4: 'F'
92 |
92 |
93 | o 3: 'D'
93 | o 3: 'D'
94 | |
94 | |
95 | o 2: 'C'
95 | o 2: 'C'
96 | |
96 | |
97 | o 1: 'B'
97 | o 1: 'B'
98 |/
98 |/
99 o 0: 'A'
99 o 0: 'A'
100
100
101 $ hg manifest
101 $ hg manifest
102 A
102 A
103 E
103 E
104 F
104 F
105 H
105 H
106
106
107 $ cd ..
107 $ cd ..
108
108
109 Rebasing G onto H with custom message:
109 Rebasing G onto H with custom message:
110
110
111 $ hg clone -q -u . a a3
111 $ hg clone -q -u . a a3
112 $ cd a3
112 $ cd a3
113
113
114 $ hg rebase --base 6 -m 'custom message'
114 $ hg rebase --base 6 -m 'custom message'
115 abort: message can only be specified with collapse
115 abort: message can only be specified with collapse
116 [255]
116 [255]
117
117
118 $ hg rebase --base 6 --collapse -m 'custom message'
118 $ hg rebase --source 4 --collapse -m 'custom message'
119 saved backup bundle to $TESTTMP/a3/.hg/strip-backup/*-backup.hg (glob)
119 saved backup bundle to $TESTTMP/a3/.hg/strip-backup/*-backup.hg (glob)
120
120
121 $ hg tglog
121 $ hg tglog
122 @ 6: 'custom message'
122 @ 6: 'custom message'
123 |
123 |
124 o 5: 'H'
124 o 5: 'H'
125 |
125 |
126 o 4: 'F'
126 o 4: 'F'
127 |
127 |
128 | o 3: 'D'
128 | o 3: 'D'
129 | |
129 | |
130 | o 2: 'C'
130 | o 2: 'C'
131 | |
131 | |
132 | o 1: 'B'
132 | o 1: 'B'
133 |/
133 |/
134 o 0: 'A'
134 o 0: 'A'
135
135
136 $ hg manifest
136 $ hg manifest
137 A
137 A
138 E
138 E
139 F
139 F
140 H
140 H
141
141
142 $ cd ..
142 $ cd ..
143
143
144 Create repo b:
144 Create repo b:
145
145
146 $ hg init b
146 $ hg init b
147 $ cd b
147 $ cd b
148
148
149 $ echo A > A
149 $ echo A > A
150 $ hg ci -Am A
150 $ hg ci -Am A
151 adding A
151 adding A
152 $ echo B > B
152 $ echo B > B
153 $ hg ci -Am B
153 $ hg ci -Am B
154 adding B
154 adding B
155
155
156 $ hg up -q 0
156 $ hg up -q 0
157
157
158 $ echo C > C
158 $ echo C > C
159 $ hg ci -Am C
159 $ hg ci -Am C
160 adding C
160 adding C
161 created new head
161 created new head
162
162
163 $ hg merge
163 $ hg merge
164 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
164 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
165 (branch merge, don't forget to commit)
165 (branch merge, don't forget to commit)
166
166
167 $ echo D > D
167 $ echo D > D
168 $ hg ci -Am D
168 $ hg ci -Am D
169 adding D
169 adding D
170
170
171 $ hg up -q 1
171 $ hg up -q 1
172
172
173 $ echo E > E
173 $ echo E > E
174 $ hg ci -Am E
174 $ hg ci -Am E
175 adding E
175 adding E
176 created new head
176 created new head
177
177
178 $ echo F > F
178 $ echo F > F
179 $ hg ci -Am F
179 $ hg ci -Am F
180 adding F
180 adding F
181
181
182 $ hg merge
182 $ hg merge
183 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
183 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
184 (branch merge, don't forget to commit)
184 (branch merge, don't forget to commit)
185 $ hg ci -m G
185 $ hg ci -m G
186
186
187 $ hg up -q 0
187 $ hg up -q 0
188
188
189 $ echo H > H
189 $ echo H > H
190 $ hg ci -Am H
190 $ hg ci -Am H
191 adding H
191 adding H
192 created new head
192 created new head
193
193
194 $ hg tglog
194 $ hg tglog
195 @ 7: 'H'
195 @ 7: 'H'
196 |
196 |
197 | o 6: 'G'
197 | o 6: 'G'
198 | |\
198 | |\
199 | | o 5: 'F'
199 | | o 5: 'F'
200 | | |
200 | | |
201 | | o 4: 'E'
201 | | o 4: 'E'
202 | | |
202 | | |
203 | o | 3: 'D'
203 | o | 3: 'D'
204 | |\|
204 | |\|
205 | o | 2: 'C'
205 | o | 2: 'C'
206 |/ /
206 |/ /
207 | o 1: 'B'
207 | o 1: 'B'
208 |/
208 |/
209 o 0: 'A'
209 o 0: 'A'
210
210
211 $ cd ..
211 $ cd ..
212
212
213
213
214 Rebase and collapse - more than one external (fail):
214 Rebase and collapse - more than one external (fail):
215
215
216 $ hg clone -q -u . b b1
216 $ hg clone -q -u . b b1
217 $ cd b1
217 $ cd b1
218
218
219 $ hg rebase -s 2 --collapse
219 $ hg rebase -s 2 --collapse
220 abort: unable to collapse, there is more than one external parent
220 abort: unable to collapse, there is more than one external parent
221 [255]
221 [255]
222
222
223 Rebase and collapse - E onto H:
223 Rebase and collapse - E onto H:
224
224
225 $ hg rebase -s 4 --collapse
225 $ hg rebase -s 4 --collapse
226 saved backup bundle to $TESTTMP/b1/.hg/strip-backup/*-backup.hg (glob)
226 saved backup bundle to $TESTTMP/b1/.hg/strip-backup/*-backup.hg (glob)
227
227
228 $ hg tglog
228 $ hg tglog
229 @ 5: 'Collapsed revision
229 @ 5: 'Collapsed revision
230 |\ * E
230 |\ * E
231 | | * F
231 | | * F
232 | | * G'
232 | | * G'
233 | o 4: 'H'
233 | o 4: 'H'
234 | |
234 | |
235 o | 3: 'D'
235 o | 3: 'D'
236 |\ \
236 |\ \
237 | o | 2: 'C'
237 | o | 2: 'C'
238 | |/
238 | |/
239 o / 1: 'B'
239 o / 1: 'B'
240 |/
240 |/
241 o 0: 'A'
241 o 0: 'A'
242
242
243 $ hg manifest
243 $ hg manifest
244 A
244 A
245 B
245 B
246 C
246 C
247 D
247 D
248 E
248 E
249 F
249 F
250 H
250 H
251
251
252 $ cd ..
252 $ cd ..
253
253
254
254
255 Create repo c:
255 Create repo c:
256
256
257 $ hg init c
257 $ hg init c
258 $ cd c
258 $ cd c
259
259
260 $ echo A > A
260 $ echo A > A
261 $ hg ci -Am A
261 $ hg ci -Am A
262 adding A
262 adding A
263 $ echo B > B
263 $ echo B > B
264 $ hg ci -Am B
264 $ hg ci -Am B
265 adding B
265 adding B
266
266
267 $ hg up -q 0
267 $ hg up -q 0
268
268
269 $ echo C > C
269 $ echo C > C
270 $ hg ci -Am C
270 $ hg ci -Am C
271 adding C
271 adding C
272 created new head
272 created new head
273
273
274 $ hg merge
274 $ hg merge
275 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
275 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
276 (branch merge, don't forget to commit)
276 (branch merge, don't forget to commit)
277
277
278 $ echo D > D
278 $ echo D > D
279 $ hg ci -Am D
279 $ hg ci -Am D
280 adding D
280 adding D
281
281
282 $ hg up -q 1
282 $ hg up -q 1
283
283
284 $ echo E > E
284 $ echo E > E
285 $ hg ci -Am E
285 $ hg ci -Am E
286 adding E
286 adding E
287 created new head
287 created new head
288 $ echo F > E
288 $ echo F > E
289 $ hg ci -m 'F'
289 $ hg ci -m 'F'
290
290
291 $ echo G > G
291 $ echo G > G
292 $ hg ci -Am G
292 $ hg ci -Am G
293 adding G
293 adding G
294
294
295 $ hg merge
295 $ hg merge
296 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
296 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
297 (branch merge, don't forget to commit)
297 (branch merge, don't forget to commit)
298
298
299 $ hg ci -m H
299 $ hg ci -m H
300
300
301 $ hg up -q 0
301 $ hg up -q 0
302
302
303 $ echo I > I
303 $ echo I > I
304 $ hg ci -Am I
304 $ hg ci -Am I
305 adding I
305 adding I
306 created new head
306 created new head
307
307
308 $ hg tglog
308 $ hg tglog
309 @ 8: 'I'
309 @ 8: 'I'
310 |
310 |
311 | o 7: 'H'
311 | o 7: 'H'
312 | |\
312 | |\
313 | | o 6: 'G'
313 | | o 6: 'G'
314 | | |
314 | | |
315 | | o 5: 'F'
315 | | o 5: 'F'
316 | | |
316 | | |
317 | | o 4: 'E'
317 | | o 4: 'E'
318 | | |
318 | | |
319 | o | 3: 'D'
319 | o | 3: 'D'
320 | |\|
320 | |\|
321 | o | 2: 'C'
321 | o | 2: 'C'
322 |/ /
322 |/ /
323 | o 1: 'B'
323 | o 1: 'B'
324 |/
324 |/
325 o 0: 'A'
325 o 0: 'A'
326
326
327 $ cd ..
327 $ cd ..
328
328
329
329
330 Rebase and collapse - E onto I:
330 Rebase and collapse - E onto I:
331
331
332 $ hg clone -q -u . c c1
332 $ hg clone -q -u . c c1
333 $ cd c1
333 $ cd c1
334
334
335 $ hg rebase -s 4 --collapse
335 $ hg rebase -s 4 --collapse
336 merging E
336 merging E
337 saved backup bundle to $TESTTMP/c1/.hg/strip-backup/*-backup.hg (glob)
337 saved backup bundle to $TESTTMP/c1/.hg/strip-backup/*-backup.hg (glob)
338
338
339 $ hg tglog
339 $ hg tglog
340 @ 5: 'Collapsed revision
340 @ 5: 'Collapsed revision
341 |\ * E
341 |\ * E
342 | | * F
342 | | * F
343 | | * G
343 | | * G
344 | | * H'
344 | | * H'
345 | o 4: 'I'
345 | o 4: 'I'
346 | |
346 | |
347 o | 3: 'D'
347 o | 3: 'D'
348 |\ \
348 |\ \
349 | o | 2: 'C'
349 | o | 2: 'C'
350 | |/
350 | |/
351 o / 1: 'B'
351 o / 1: 'B'
352 |/
352 |/
353 o 0: 'A'
353 o 0: 'A'
354
354
355 $ hg manifest
355 $ hg manifest
356 A
356 A
357 B
357 B
358 C
358 C
359 D
359 D
360 E
360 E
361 G
361 G
362 I
362 I
363
363
364 $ cat E
364 $ cat E
365 F
365 F
366
366
367 $ cd ..
367 $ cd ..
368
368
369
369
370 Create repo d:
370 Create repo d:
371
371
372 $ hg init d
372 $ hg init d
373 $ cd d
373 $ cd d
374
374
375 $ echo A > A
375 $ echo A > A
376 $ hg ci -Am A
376 $ hg ci -Am A
377 adding A
377 adding A
378 $ echo B > B
378 $ echo B > B
379 $ hg ci -Am B
379 $ hg ci -Am B
380 adding B
380 adding B
381 $ echo C > C
381 $ echo C > C
382 $ hg ci -Am C
382 $ hg ci -Am C
383 adding C
383 adding C
384
384
385 $ hg up -q 1
385 $ hg up -q 1
386
386
387 $ echo D > D
387 $ echo D > D
388 $ hg ci -Am D
388 $ hg ci -Am D
389 adding D
389 adding D
390 created new head
390 created new head
391 $ hg merge
391 $ hg merge
392 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
392 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
393 (branch merge, don't forget to commit)
393 (branch merge, don't forget to commit)
394
394
395 $ hg ci -m E
395 $ hg ci -m E
396
396
397 $ hg up -q 0
397 $ hg up -q 0
398
398
399 $ echo F > F
399 $ echo F > F
400 $ hg ci -Am F
400 $ hg ci -Am F
401 adding F
401 adding F
402 created new head
402 created new head
403
403
404 $ hg tglog
404 $ hg tglog
405 @ 5: 'F'
405 @ 5: 'F'
406 |
406 |
407 | o 4: 'E'
407 | o 4: 'E'
408 | |\
408 | |\
409 | | o 3: 'D'
409 | | o 3: 'D'
410 | | |
410 | | |
411 | o | 2: 'C'
411 | o | 2: 'C'
412 | |/
412 | |/
413 | o 1: 'B'
413 | o 1: 'B'
414 |/
414 |/
415 o 0: 'A'
415 o 0: 'A'
416
416
417 $ cd ..
417 $ cd ..
418
418
419
419
420 Rebase and collapse - B onto F:
420 Rebase and collapse - B onto F:
421
421
422 $ hg clone -q -u . d d1
422 $ hg clone -q -u . d d1
423 $ cd d1
423 $ cd d1
424
424
425 $ hg rebase -s 1 --collapse
425 $ hg rebase -s 1 --collapse
426 saved backup bundle to $TESTTMP/d1/.hg/strip-backup/*-backup.hg (glob)
426 saved backup bundle to $TESTTMP/d1/.hg/strip-backup/*-backup.hg (glob)
427
427
428 $ hg tglog
428 $ hg tglog
429 @ 2: 'Collapsed revision
429 @ 2: 'Collapsed revision
430 | * B
430 | * B
431 | * C
431 | * C
432 | * D
432 | * D
433 | * E'
433 | * E'
434 o 1: 'F'
434 o 1: 'F'
435 |
435 |
436 o 0: 'A'
436 o 0: 'A'
437
437
438 $ hg manifest
438 $ hg manifest
439 A
439 A
440 B
440 B
441 C
441 C
442 D
442 D
443 F
443 F
444
444
445 Interactions between collapse and keepbranches
445 Interactions between collapse and keepbranches
446 $ cd ..
446 $ cd ..
447 $ hg init e
447 $ hg init e
448 $ cd e
448 $ cd e
449 $ echo 'a' > a
449 $ echo 'a' > a
450 $ hg ci -Am 'A'
450 $ hg ci -Am 'A'
451 adding a
451 adding a
452
452
453 $ hg branch '1'
453 $ hg branch '1'
454 marked working directory as branch 1
454 marked working directory as branch 1
455 $ echo 'b' > b
455 $ echo 'b' > b
456 $ hg ci -Am 'B'
456 $ hg ci -Am 'B'
457 adding b
457 adding b
458
458
459 $ hg branch '2'
459 $ hg branch '2'
460 marked working directory as branch 2
460 marked working directory as branch 2
461 $ echo 'c' > c
461 $ echo 'c' > c
462 $ hg ci -Am 'C'
462 $ hg ci -Am 'C'
463 adding c
463 adding c
464
464
465 $ hg up -q 0
465 $ hg up -q 0
466 $ echo 'd' > d
466 $ echo 'd' > d
467 $ hg ci -Am 'D'
467 $ hg ci -Am 'D'
468 adding d
468 adding d
469
469
470 $ hg tglog
470 $ hg tglog
471 @ 3: 'D'
471 @ 3: 'D'
472 |
472 |
473 | o 2: 'C' 2
473 | o 2: 'C' 2
474 | |
474 | |
475 | o 1: 'B' 1
475 | o 1: 'B' 1
476 |/
476 |/
477 o 0: 'A'
477 o 0: 'A'
478
478
479 $ hg rebase --keepbranches --collapse -s 1 -d 3
479 $ hg rebase --keepbranches --collapse -s 1 -d 3
480 abort: cannot collapse multiple named branches
480 abort: cannot collapse multiple named branches
481 [255]
481 [255]
482
482
@@ -1,389 +1,389
1 $ cat >> $HGRCPATH <<EOF
1 $ cat >> $HGRCPATH <<EOF
2 > [extensions]
2 > [extensions]
3 > graphlog=
3 > graphlog=
4 > rebase=
4 > rebase=
5 >
5 >
6 > [alias]
6 > [alias]
7 > tglog = log -G --template "{rev}: '{desc}' {branches}\n"
7 > tglog = log -G --template "{rev}: '{desc}' {branches}\n"
8 > EOF
8 > EOF
9
9
10
10
11 $ hg init a
11 $ hg init a
12 $ cd a
12 $ cd a
13 $ hg unbundle $TESTDIR/bundles/rebase.hg
13 $ hg unbundle $TESTDIR/bundles/rebase.hg
14 adding changesets
14 adding changesets
15 adding manifests
15 adding manifests
16 adding file changes
16 adding file changes
17 added 8 changesets with 7 changes to 7 files (+2 heads)
17 added 8 changesets with 7 changes to 7 files (+2 heads)
18 (run 'hg heads' to see heads, 'hg merge' to merge)
18 (run 'hg heads' to see heads, 'hg merge' to merge)
19 $ hg up tip
19 $ hg up tip
20 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
20 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
21
21
22 $ echo I > I
22 $ echo I > I
23 $ hg ci -AmI
23 $ hg ci -AmI
24 adding I
24 adding I
25
25
26 $ hg tglog
26 $ hg tglog
27 @ 8: 'I'
27 @ 8: 'I'
28 |
28 |
29 o 7: 'H'
29 o 7: 'H'
30 |
30 |
31 | o 6: 'G'
31 | o 6: 'G'
32 |/|
32 |/|
33 o | 5: 'F'
33 o | 5: 'F'
34 | |
34 | |
35 | o 4: 'E'
35 | o 4: 'E'
36 |/
36 |/
37 | o 3: 'D'
37 | o 3: 'D'
38 | |
38 | |
39 | o 2: 'C'
39 | o 2: 'C'
40 | |
40 | |
41 | o 1: 'B'
41 | o 1: 'B'
42 |/
42 |/
43 o 0: 'A'
43 o 0: 'A'
44
44
45 $ cd ..
45 $ cd ..
46
46
47
47
48 These fail:
48 These fail:
49
49
50 $ hg clone -q -u . a a1
50 $ hg clone -q -u . a a1
51 $ cd a1
51 $ cd a1
52
52
53 $ hg rebase -s 8 -d 7
53 $ hg rebase -s 8 -d 7
54 abort: source is a child of destination
54 nothing to rebase
55 [255]
55 [1]
56
56
57 $ hg rebase --continue --abort
57 $ hg rebase --continue --abort
58 abort: cannot use both abort and continue
58 abort: cannot use both abort and continue
59 [255]
59 [255]
60
60
61 $ hg rebase --continue --collapse
61 $ hg rebase --continue --collapse
62 abort: cannot use collapse with continue or abort
62 abort: cannot use collapse with continue or abort
63 [255]
63 [255]
64
64
65 $ hg rebase --continue --dest 4
65 $ hg rebase --continue --dest 4
66 abort: abort and continue do not allow specifying revisions
66 abort: abort and continue do not allow specifying revisions
67 [255]
67 [255]
68
68
69 $ hg rebase --base 5 --source 4
69 $ hg rebase --base 5 --source 4
70 abort: cannot specify both a revision and a base
70 abort: cannot specify both a revision and a base
71 [255]
71 [255]
72
72
73 $ hg rebase
73 $ hg rebase
74 nothing to rebase
74 nothing to rebase
75 [1]
75 [1]
76
76
77 $ hg up -q 7
77 $ hg up -q 7
78
78
79 $ hg rebase
79 $ hg rebase --traceback
80 nothing to rebase
80 nothing to rebase
81 [1]
81 [1]
82
82
83
83
84 These work:
84 These work:
85
85
86 Rebase with no arguments (from 3 onto 8):
86 Rebase with no arguments (from 3 onto 8):
87
87
88 $ hg up -q -C 3
88 $ hg up -q -C 3
89
89
90 $ hg rebase
90 $ hg rebase
91 saved backup bundle to $TESTTMP/a1/.hg/strip-backup/*-backup.hg (glob)
91 saved backup bundle to $TESTTMP/a1/.hg/strip-backup/*-backup.hg (glob)
92
92
93 $ hg tglog
93 $ hg tglog
94 @ 8: 'D'
94 @ 8: 'D'
95 |
95 |
96 o 7: 'C'
96 o 7: 'C'
97 |
97 |
98 o 6: 'B'
98 o 6: 'B'
99 |
99 |
100 o 5: 'I'
100 o 5: 'I'
101 |
101 |
102 o 4: 'H'
102 o 4: 'H'
103 |
103 |
104 | o 3: 'G'
104 | o 3: 'G'
105 |/|
105 |/|
106 o | 2: 'F'
106 o | 2: 'F'
107 | |
107 | |
108 | o 1: 'E'
108 | o 1: 'E'
109 |/
109 |/
110 o 0: 'A'
110 o 0: 'A'
111
111
112 Try to rollback after a rebase (fail):
112 Try to rollback after a rebase (fail):
113
113
114 $ hg rollback
114 $ hg rollback
115 no rollback information available
115 no rollback information available
116 [1]
116 [1]
117
117
118 $ cd ..
118 $ cd ..
119
119
120
120
121 Rebase with base == '.' => same as no arguments (from 3 onto 8):
121 Rebase with base == '.' => same as no arguments (from 3 onto 8):
122
122
123 $ hg clone -q -u 3 a a2
123 $ hg clone -q -u 3 a a2
124 $ cd a2
124 $ cd a2
125
125
126 $ hg rebase --base .
126 $ hg rebase --base .
127 saved backup bundle to $TESTTMP/a2/.hg/strip-backup/*-backup.hg (glob)
127 saved backup bundle to $TESTTMP/a2/.hg/strip-backup/*-backup.hg (glob)
128
128
129 $ hg tglog
129 $ hg tglog
130 @ 8: 'D'
130 @ 8: 'D'
131 |
131 |
132 o 7: 'C'
132 o 7: 'C'
133 |
133 |
134 o 6: 'B'
134 o 6: 'B'
135 |
135 |
136 o 5: 'I'
136 o 5: 'I'
137 |
137 |
138 o 4: 'H'
138 o 4: 'H'
139 |
139 |
140 | o 3: 'G'
140 | o 3: 'G'
141 |/|
141 |/|
142 o | 2: 'F'
142 o | 2: 'F'
143 | |
143 | |
144 | o 1: 'E'
144 | o 1: 'E'
145 |/
145 |/
146 o 0: 'A'
146 o 0: 'A'
147
147
148 $ cd ..
148 $ cd ..
149
149
150
150
151 Rebase with dest == `hg branch` => same as no arguments (from 3 onto 8):
151 Rebase with dest == `hg branch` => same as no arguments (from 3 onto 8):
152
152
153 $ hg clone -q -u 3 a a3
153 $ hg clone -q -u 3 a a3
154 $ cd a3
154 $ cd a3
155
155
156 $ hg rebase --dest `hg branch`
156 $ hg rebase --dest `hg branch`
157 saved backup bundle to $TESTTMP/a3/.hg/strip-backup/*-backup.hg (glob)
157 saved backup bundle to $TESTTMP/a3/.hg/strip-backup/*-backup.hg (glob)
158
158
159 $ hg tglog
159 $ hg tglog
160 @ 8: 'D'
160 @ 8: 'D'
161 |
161 |
162 o 7: 'C'
162 o 7: 'C'
163 |
163 |
164 o 6: 'B'
164 o 6: 'B'
165 |
165 |
166 o 5: 'I'
166 o 5: 'I'
167 |
167 |
168 o 4: 'H'
168 o 4: 'H'
169 |
169 |
170 | o 3: 'G'
170 | o 3: 'G'
171 |/|
171 |/|
172 o | 2: 'F'
172 o | 2: 'F'
173 | |
173 | |
174 | o 1: 'E'
174 | o 1: 'E'
175 |/
175 |/
176 o 0: 'A'
176 o 0: 'A'
177
177
178 $ cd ..
178 $ cd ..
179
179
180
180
181 Specify only source (from 2 onto 8):
181 Specify only source (from 2 onto 8):
182
182
183 $ hg clone -q -u . a a4
183 $ hg clone -q -u . a a4
184 $ cd a4
184 $ cd a4
185
185
186 $ hg rebase --source 2
186 $ hg rebase --source 2
187 saved backup bundle to $TESTTMP/a4/.hg/strip-backup/*-backup.hg (glob)
187 saved backup bundle to $TESTTMP/a4/.hg/strip-backup/*-backup.hg (glob)
188
188
189 $ hg tglog
189 $ hg tglog
190 @ 8: 'D'
190 @ 8: 'D'
191 |
191 |
192 o 7: 'C'
192 o 7: 'C'
193 |\
193 |\
194 | o 6: 'I'
194 | o 6: 'I'
195 | |
195 | |
196 | o 5: 'H'
196 | o 5: 'H'
197 | |
197 | |
198 | | o 4: 'G'
198 | | o 4: 'G'
199 | |/|
199 | |/|
200 | o | 3: 'F'
200 | o | 3: 'F'
201 | | |
201 | | |
202 | | o 2: 'E'
202 | | o 2: 'E'
203 | |/
203 | |/
204 o | 1: 'B'
204 o | 1: 'B'
205 |/
205 |/
206 o 0: 'A'
206 o 0: 'A'
207
207
208 $ cd ..
208 $ cd ..
209
209
210
210
211 Specify only dest (from 3 onto 6):
211 Specify only dest (from 3 onto 6):
212
212
213 $ hg clone -q -u 3 a a5
213 $ hg clone -q -u 3 a a5
214 $ cd a5
214 $ cd a5
215
215
216 $ hg rebase --dest 6
216 $ hg rebase --dest 6
217 saved backup bundle to $TESTTMP/a5/.hg/strip-backup/*-backup.hg (glob)
217 saved backup bundle to $TESTTMP/a5/.hg/strip-backup/*-backup.hg (glob)
218
218
219 $ hg tglog
219 $ hg tglog
220 @ 8: 'D'
220 @ 8: 'D'
221 |
221 |
222 o 7: 'C'
222 o 7: 'C'
223 |
223 |
224 o 6: 'B'
224 o 6: 'B'
225 |
225 |
226 | o 5: 'I'
226 | o 5: 'I'
227 | |
227 | |
228 | o 4: 'H'
228 | o 4: 'H'
229 | |
229 | |
230 o | 3: 'G'
230 o | 3: 'G'
231 |\|
231 |\|
232 | o 2: 'F'
232 | o 2: 'F'
233 | |
233 | |
234 o | 1: 'E'
234 o | 1: 'E'
235 |/
235 |/
236 o 0: 'A'
236 o 0: 'A'
237
237
238 $ cd ..
238 $ cd ..
239
239
240
240
241 Specify only base (from 1 onto 8):
241 Specify only base (from 1 onto 8):
242
242
243 $ hg clone -q -u . a a6
243 $ hg clone -q -u . a a6
244 $ cd a6
244 $ cd a6
245
245
246 $ hg rebase --base 3
246 $ hg rebase --base 3
247 saved backup bundle to $TESTTMP/a6/.hg/strip-backup/*-backup.hg (glob)
247 saved backup bundle to $TESTTMP/a6/.hg/strip-backup/*-backup.hg (glob)
248
248
249 $ hg tglog
249 $ hg tglog
250 @ 8: 'D'
250 @ 8: 'D'
251 |
251 |
252 o 7: 'C'
252 o 7: 'C'
253 |
253 |
254 o 6: 'B'
254 o 6: 'B'
255 |
255 |
256 o 5: 'I'
256 o 5: 'I'
257 |
257 |
258 o 4: 'H'
258 o 4: 'H'
259 |
259 |
260 | o 3: 'G'
260 | o 3: 'G'
261 |/|
261 |/|
262 o | 2: 'F'
262 o | 2: 'F'
263 | |
263 | |
264 | o 1: 'E'
264 | o 1: 'E'
265 |/
265 |/
266 o 0: 'A'
266 o 0: 'A'
267
267
268 $ cd ..
268 $ cd ..
269
269
270
270
271 Specify source and dest (from 2 onto 7):
271 Specify source and dest (from 2 onto 7):
272
272
273 $ hg clone -q -u . a a7
273 $ hg clone -q -u . a a7
274 $ cd a7
274 $ cd a7
275
275
276 $ hg rebase --detach --source 2 --dest 7
276 $ hg rebase --detach --source 2 --dest 7
277 saved backup bundle to $TESTTMP/a7/.hg/strip-backup/*-backup.hg (glob)
277 saved backup bundle to $TESTTMP/a7/.hg/strip-backup/*-backup.hg (glob)
278
278
279 $ hg tglog
279 $ hg tglog
280 @ 8: 'D'
280 @ 8: 'D'
281 |
281 |
282 o 7: 'C'
282 o 7: 'C'
283 |
283 |
284 | o 6: 'I'
284 | o 6: 'I'
285 |/
285 |/
286 o 5: 'H'
286 o 5: 'H'
287 |
287 |
288 | o 4: 'G'
288 | o 4: 'G'
289 |/|
289 |/|
290 o | 3: 'F'
290 o | 3: 'F'
291 | |
291 | |
292 | o 2: 'E'
292 | o 2: 'E'
293 |/
293 |/
294 | o 1: 'B'
294 | o 1: 'B'
295 |/
295 |/
296 o 0: 'A'
296 o 0: 'A'
297
297
298 $ cd ..
298 $ cd ..
299
299
300
300
301 Specify base and dest (from 1 onto 7):
301 Specify base and dest (from 1 onto 7):
302
302
303 $ hg clone -q -u . a a8
303 $ hg clone -q -u . a a8
304 $ cd a8
304 $ cd a8
305
305
306 $ hg rebase --base 3 --dest 7
306 $ hg rebase --base 3 --dest 7
307 saved backup bundle to $TESTTMP/a8/.hg/strip-backup/*-backup.hg (glob)
307 saved backup bundle to $TESTTMP/a8/.hg/strip-backup/*-backup.hg (glob)
308
308
309 $ hg tglog
309 $ hg tglog
310 @ 8: 'D'
310 @ 8: 'D'
311 |
311 |
312 o 7: 'C'
312 o 7: 'C'
313 |
313 |
314 o 6: 'B'
314 o 6: 'B'
315 |
315 |
316 | o 5: 'I'
316 | o 5: 'I'
317 |/
317 |/
318 o 4: 'H'
318 o 4: 'H'
319 |
319 |
320 | o 3: 'G'
320 | o 3: 'G'
321 |/|
321 |/|
322 o | 2: 'F'
322 o | 2: 'F'
323 | |
323 | |
324 | o 1: 'E'
324 | o 1: 'E'
325 |/
325 |/
326 o 0: 'A'
326 o 0: 'A'
327
327
328 $ cd ..
328 $ cd ..
329
329
330 Test --tool parameter:
330 Test --tool parameter:
331
331
332 $ hg init b
332 $ hg init b
333 $ cd b
333 $ cd b
334
334
335 $ echo c1 > c1
335 $ echo c1 > c1
336 $ hg ci -Am c1
336 $ hg ci -Am c1
337 adding c1
337 adding c1
338
338
339 $ echo c2 > c2
339 $ echo c2 > c2
340 $ hg ci -Am c2
340 $ hg ci -Am c2
341 adding c2
341 adding c2
342
342
343 $ hg up -q 0
343 $ hg up -q 0
344 $ echo c2b > c2
344 $ echo c2b > c2
345 $ hg ci -Am c2b
345 $ hg ci -Am c2b
346 adding c2
346 adding c2
347 created new head
347 created new head
348
348
349 $ cd ..
349 $ cd ..
350
350
351 $ hg clone -q -u . b b1
351 $ hg clone -q -u . b b1
352 $ cd b1
352 $ cd b1
353
353
354 $ hg rebase -s 2 -d 1 --tool internal:local
354 $ hg rebase -s 2 -d 1 --tool internal:local
355 saved backup bundle to $TESTTMP/b1/.hg/strip-backup/*-backup.hg (glob)
355 saved backup bundle to $TESTTMP/b1/.hg/strip-backup/*-backup.hg (glob)
356
356
357 $ hg cat c2
357 $ hg cat c2
358 c2
358 c2
359
359
360 $ cd ..
360 $ cd ..
361
361
362
362
363 $ hg clone -q -u . b b2
363 $ hg clone -q -u . b b2
364 $ cd b2
364 $ cd b2
365
365
366 $ hg rebase -s 2 -d 1 --tool internal:other
366 $ hg rebase -s 2 -d 1 --tool internal:other
367 saved backup bundle to $TESTTMP/b2/.hg/strip-backup/*-backup.hg (glob)
367 saved backup bundle to $TESTTMP/b2/.hg/strip-backup/*-backup.hg (glob)
368
368
369 $ hg cat c2
369 $ hg cat c2
370 c2b
370 c2b
371
371
372 $ cd ..
372 $ cd ..
373
373
374
374
375 $ hg clone -q -u . b b3
375 $ hg clone -q -u . b b3
376 $ cd b3
376 $ cd b3
377
377
378 $ hg rebase -s 2 -d 1 --tool internal:fail
378 $ hg rebase -s 2 -d 1 --tool internal:fail
379 abort: unresolved conflicts (see hg resolve, then hg rebase --continue)
379 abort: unresolved conflicts (see hg resolve, then hg rebase --continue)
380 [255]
380 [255]
381
381
382 $ hg resolve -l
382 $ hg resolve -l
383 U c2
383 U c2
384
384
385 $ hg resolve -m c2
385 $ hg resolve -m c2
386 $ hg rebase -c --tool internal:fail
386 $ hg rebase -c --tool internal:fail
387 tool option will be ignored
387 tool option will be ignored
388 saved backup bundle to $TESTTMP/b3/.hg/strip-backup/*-backup.hg (glob)
388 saved backup bundle to $TESTTMP/b3/.hg/strip-backup/*-backup.hg (glob)
389
389
@@ -1,272 +1,272
1 $ cat >> $HGRCPATH <<EOF
1 $ cat >> $HGRCPATH <<EOF
2 > [extensions]
2 > [extensions]
3 > graphlog=
3 > graphlog=
4 > rebase=
4 > rebase=
5 >
5 >
6 > [alias]
6 > [alias]
7 > tglog = log -G --template "{rev}: '{desc}' {branches}\n"
7 > tglog = log -G --template "{rev}: '{desc}' {branches}\n"
8 > EOF
8 > EOF
9
9
10
10
11 $ hg init a
11 $ hg init a
12 $ cd a
12 $ cd a
13 $ hg unbundle $TESTDIR/bundles/rebase.hg
13 $ hg unbundle $TESTDIR/bundles/rebase.hg
14 adding changesets
14 adding changesets
15 adding manifests
15 adding manifests
16 adding file changes
16 adding file changes
17 added 8 changesets with 7 changes to 7 files (+2 heads)
17 added 8 changesets with 7 changes to 7 files (+2 heads)
18 (run 'hg heads' to see heads, 'hg merge' to merge)
18 (run 'hg heads' to see heads, 'hg merge' to merge)
19 $ hg up tip
19 $ hg up tip
20 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
20 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
21 $ cd ..
21 $ cd ..
22
22
23
23
24 Rebasing
24 Rebasing
25 D onto H - simple rebase:
25 D onto H - simple rebase:
26
26
27 $ hg clone -q -u . a a1
27 $ hg clone -q -u . a a1
28 $ cd a1
28 $ cd a1
29
29
30 $ hg tglog
30 $ hg tglog
31 @ 7: 'H'
31 @ 7: 'H'
32 |
32 |
33 | o 6: 'G'
33 | o 6: 'G'
34 |/|
34 |/|
35 o | 5: 'F'
35 o | 5: 'F'
36 | |
36 | |
37 | o 4: 'E'
37 | o 4: 'E'
38 |/
38 |/
39 | o 3: 'D'
39 | o 3: 'D'
40 | |
40 | |
41 | o 2: 'C'
41 | o 2: 'C'
42 | |
42 | |
43 | o 1: 'B'
43 | o 1: 'B'
44 |/
44 |/
45 o 0: 'A'
45 o 0: 'A'
46
46
47
47
48 $ hg rebase -s 3 -d 7
48 $ hg rebase -s 3 -d 7
49 saved backup bundle to $TESTTMP/a1/.hg/strip-backup/*-backup.hg (glob)
49 saved backup bundle to $TESTTMP/a1/.hg/strip-backup/*-backup.hg (glob)
50
50
51 $ hg tglog
51 $ hg tglog
52 @ 7: 'D'
52 @ 7: 'D'
53 |\
53 |\
54 | o 6: 'H'
54 | o 6: 'H'
55 | |
55 | |
56 | | o 5: 'G'
56 | | o 5: 'G'
57 | |/|
57 | |/|
58 | o | 4: 'F'
58 | o | 4: 'F'
59 | | |
59 | | |
60 | | o 3: 'E'
60 | | o 3: 'E'
61 | |/
61 | |/
62 o | 2: 'C'
62 o | 2: 'C'
63 | |
63 | |
64 o | 1: 'B'
64 o | 1: 'B'
65 |/
65 |/
66 o 0: 'A'
66 o 0: 'A'
67
67
68 $ cd ..
68 $ cd ..
69
69
70
70
71 D onto F - intermediate point:
71 D onto F - intermediate point:
72
72
73 $ hg clone -q -u . a a2
73 $ hg clone -q -u . a a2
74 $ cd a2
74 $ cd a2
75
75
76 $ hg rebase -s 3 -d 5
76 $ hg rebase -s 3 -d 5
77 saved backup bundle to $TESTTMP/a2/.hg/strip-backup/*-backup.hg (glob)
77 saved backup bundle to $TESTTMP/a2/.hg/strip-backup/*-backup.hg (glob)
78
78
79 $ hg tglog
79 $ hg tglog
80 @ 7: 'D'
80 @ 7: 'D'
81 |\
81 |\
82 | | o 6: 'H'
82 | | o 6: 'H'
83 | |/
83 | |/
84 | | o 5: 'G'
84 | | o 5: 'G'
85 | |/|
85 | |/|
86 | o | 4: 'F'
86 | o | 4: 'F'
87 | | |
87 | | |
88 | | o 3: 'E'
88 | | o 3: 'E'
89 | |/
89 | |/
90 o | 2: 'C'
90 o | 2: 'C'
91 | |
91 | |
92 o | 1: 'B'
92 o | 1: 'B'
93 |/
93 |/
94 o 0: 'A'
94 o 0: 'A'
95
95
96 $ cd ..
96 $ cd ..
97
97
98
98
99 E onto H - skip of G:
99 E onto H - skip of G:
100
100
101 $ hg clone -q -u . a a3
101 $ hg clone -q -u . a a3
102 $ cd a3
102 $ cd a3
103
103
104 $ hg rebase -s 4 -d 7
104 $ hg rebase -s 4 -d 7
105 saved backup bundle to $TESTTMP/a3/.hg/strip-backup/*-backup.hg (glob)
105 saved backup bundle to $TESTTMP/a3/.hg/strip-backup/*-backup.hg (glob)
106
106
107 $ hg tglog
107 $ hg tglog
108 @ 6: 'E'
108 @ 6: 'E'
109 |
109 |
110 o 5: 'H'
110 o 5: 'H'
111 |
111 |
112 o 4: 'F'
112 o 4: 'F'
113 |
113 |
114 | o 3: 'D'
114 | o 3: 'D'
115 | |
115 | |
116 | o 2: 'C'
116 | o 2: 'C'
117 | |
117 | |
118 | o 1: 'B'
118 | o 1: 'B'
119 |/
119 |/
120 o 0: 'A'
120 o 0: 'A'
121
121
122 $ cd ..
122 $ cd ..
123
123
124
124
125 F onto E - rebase of a branching point (skip G):
125 F onto E - rebase of a branching point (skip G):
126
126
127 $ hg clone -q -u . a a4
127 $ hg clone -q -u . a a4
128 $ cd a4
128 $ cd a4
129
129
130 $ hg rebase -s 5 -d 4
130 $ hg rebase -s 5 -d 4
131 saved backup bundle to $TESTTMP/a4/.hg/strip-backup/*-backup.hg (glob)
131 saved backup bundle to $TESTTMP/a4/.hg/strip-backup/*-backup.hg (glob)
132
132
133 $ hg tglog
133 $ hg tglog
134 @ 6: 'H'
134 @ 6: 'H'
135 |
135 |
136 o 5: 'F'
136 o 5: 'F'
137 |
137 |
138 o 4: 'E'
138 o 4: 'E'
139 |
139 |
140 | o 3: 'D'
140 | o 3: 'D'
141 | |
141 | |
142 | o 2: 'C'
142 | o 2: 'C'
143 | |
143 | |
144 | o 1: 'B'
144 | o 1: 'B'
145 |/
145 |/
146 o 0: 'A'
146 o 0: 'A'
147
147
148 $ cd ..
148 $ cd ..
149
149
150
150
151 G onto H - merged revision having a parent in ancestors of target:
151 G onto H - merged revision having a parent in ancestors of target:
152
152
153 $ hg clone -q -u . a a5
153 $ hg clone -q -u . a a5
154 $ cd a5
154 $ cd a5
155
155
156 $ hg rebase -s 6 -d 7
156 $ hg rebase -s 6 -d 7
157 saved backup bundle to $TESTTMP/a5/.hg/strip-backup/*-backup.hg (glob)
157 saved backup bundle to $TESTTMP/a5/.hg/strip-backup/*-backup.hg (glob)
158
158
159 $ hg tglog
159 $ hg tglog
160 @ 7: 'G'
160 @ 7: 'G'
161 |\
161 |\
162 | o 6: 'H'
162 | o 6: 'H'
163 | |
163 | |
164 | o 5: 'F'
164 | o 5: 'F'
165 | |
165 | |
166 o | 4: 'E'
166 o | 4: 'E'
167 |/
167 |/
168 | o 3: 'D'
168 | o 3: 'D'
169 | |
169 | |
170 | o 2: 'C'
170 | o 2: 'C'
171 | |
171 | |
172 | o 1: 'B'
172 | o 1: 'B'
173 |/
173 |/
174 o 0: 'A'
174 o 0: 'A'
175
175
176 $ cd ..
176 $ cd ..
177
177
178
178
179 F onto B - G maintains E as parent:
179 F onto B - G maintains E as parent:
180
180
181 $ hg clone -q -u . a a6
181 $ hg clone -q -u . a a6
182 $ cd a6
182 $ cd a6
183
183
184 $ hg rebase -s 5 -d 1
184 $ hg rebase -s 5 -d 1
185 saved backup bundle to $TESTTMP/a6/.hg/strip-backup/*-backup.hg (glob)
185 saved backup bundle to $TESTTMP/a6/.hg/strip-backup/*-backup.hg (glob)
186
186
187 $ hg tglog
187 $ hg tglog
188 @ 7: 'H'
188 @ 7: 'H'
189 |
189 |
190 | o 6: 'G'
190 | o 6: 'G'
191 |/|
191 |/|
192 o | 5: 'F'
192 o | 5: 'F'
193 | |
193 | |
194 | o 4: 'E'
194 | o 4: 'E'
195 | |
195 | |
196 | | o 3: 'D'
196 | | o 3: 'D'
197 | | |
197 | | |
198 +---o 2: 'C'
198 +---o 2: 'C'
199 | |
199 | |
200 o | 1: 'B'
200 o | 1: 'B'
201 |/
201 |/
202 o 0: 'A'
202 o 0: 'A'
203
203
204 $ cd ..
204 $ cd ..
205
205
206
206
207 These will fail (using --source):
207 These will fail (using --source):
208
208
209 G onto F - rebase onto an ancestor:
209 G onto F - rebase onto an ancestor:
210
210
211 $ hg clone -q -u . a a7
211 $ hg clone -q -u . a a7
212 $ cd a7
212 $ cd a7
213
213
214 $ hg rebase -s 6 -d 5
214 $ hg rebase -s 6 -d 5
215 abort: source is a child of destination
215 nothing to rebase
216 [255]
216 [1]
217
217
218 F onto G - rebase onto a descendant:
218 F onto G - rebase onto a descendant:
219
219
220 $ hg rebase -s 5 -d 6
220 $ hg rebase -s 5 -d 6
221 abort: source is ancestor of destination
221 abort: source is ancestor of destination
222 [255]
222 [255]
223
223
224 G onto B - merge revision with both parents not in ancestors of target:
224 G onto B - merge revision with both parents not in ancestors of target:
225
225
226 $ hg rebase -s 6 -d 1
226 $ hg rebase -s 6 -d 1
227 abort: cannot use revision 6 as base, result would have 3 parents
227 abort: cannot use revision 6 as base, result would have 3 parents
228 [255]
228 [255]
229
229
230
230
231 These will abort gracefully (using --base):
231 These will abort gracefully (using --base):
232
232
233 G onto G - rebase onto same changeset:
233 G onto G - rebase onto same changeset:
234
234
235 $ hg rebase -b 6 -d 6
235 $ hg rebase -b 6 -d 6
236 nothing to rebase
236 nothing to rebase
237 [1]
237 [1]
238
238
239 G onto F - rebase onto an ancestor:
239 G onto F - rebase onto an ancestor:
240
240
241 $ hg rebase -b 6 -d 5
241 $ hg rebase -b 6 -d 5
242 nothing to rebase
242 nothing to rebase
243 [1]
243 [1]
244
244
245 F onto G - rebase onto a descendant:
245 F onto G - rebase onto a descendant:
246
246
247 $ hg rebase -b 5 -d 6
247 $ hg rebase -b 5 -d 6
248 nothing to rebase
248 nothing to rebase
249 [1]
249 [1]
250
250
251 C onto A - rebase onto an ancestor:
251 C onto A - rebase onto an ancestor:
252
252
253 $ hg rebase -d 0 -s 2
253 $ hg rebase -d 0 -s 2
254 saved backup bundle to $TESTTMP/a7/.hg/strip-backup/5fddd98957c8-backup.hg
254 saved backup bundle to $TESTTMP/a7/.hg/strip-backup/5fddd98957c8-backup.hg
255 $ hg tglog
255 $ hg tglog
256 @ 7: 'D'
256 @ 7: 'D'
257 |
257 |
258 o 6: 'C'
258 o 6: 'C'
259 |
259 |
260 | o 5: 'H'
260 | o 5: 'H'
261 | |
261 | |
262 | | o 4: 'G'
262 | | o 4: 'G'
263 | |/|
263 | |/|
264 | o | 3: 'F'
264 | o | 3: 'F'
265 |/ /
265 |/ /
266 | o 2: 'E'
266 | o 2: 'E'
267 |/
267 |/
268 | o 1: 'B'
268 | o 1: 'B'
269 |/
269 |/
270 o 0: 'A'
270 o 0: 'A'
271
271
272
272
General Comments 0
You need to be logged in to leave comments. Login now