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