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