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