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