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