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