Show More
@@ -574,9 +574,9 b' def abort(repo, originalwd, target, stat' | |||||
574 | merge.update(repo, repo[originalwd].rev(), False, True, False) |
|
574 | merge.update(repo, repo[originalwd].rev(), False, True, False) | |
575 | rebased = filter(lambda x: x > -1 and x != target, state.values()) |
|
575 | rebased = filter(lambda x: x > -1 and x != target, state.values()) | |
576 | if rebased: |
|
576 | if rebased: | |
577 |
strippoint = |
|
577 | strippoints = [c.node() for c in repo.set('roots(%ld)', rebased)] | |
578 | # no backup of rebased cset versions needed |
|
578 | # no backup of rebased cset versions needed | |
579 |
repair.strip(repo.ui, repo, |
|
579 | repair.strip(repo.ui, repo, strippoints) | |
580 | clearstatus(repo) |
|
580 | clearstatus(repo) | |
581 | repo.ui.warn(_('rebase aborted\n')) |
|
581 | repo.ui.warn(_('rebase aborted\n')) | |
582 | return 0 |
|
582 | return 0 | |
@@ -599,65 +599,65 b' def buildstate(repo, dest, rebaseset, co' | |||||
599 | roots = list(repo.set('roots(%ld)', rebaseset)) |
|
599 | roots = list(repo.set('roots(%ld)', rebaseset)) | |
600 | if not roots: |
|
600 | if not roots: | |
601 | raise util.Abort(_('no matching revisions')) |
|
601 | raise util.Abort(_('no matching revisions')) | |
602 | if len(roots) > 1: |
|
602 | roots.sort() | |
603 | raise util.Abort(_("can't rebase multiple roots")) |
|
603 | state = {} | |
604 | root = roots[0] |
|
604 | detachset = set() | |
605 |
|
605 | for root in roots: | ||
606 | commonbase = root.ancestor(dest) |
|
606 | commonbase = root.ancestor(dest) | |
607 | if commonbase == root: |
|
607 | if commonbase == root: | |
608 | raise util.Abort(_('source is ancestor of destination')) |
|
608 | raise util.Abort(_('source is ancestor of destination')) | |
609 | if commonbase == dest: |
|
609 | if commonbase == dest: | |
610 | samebranch = root.branch() == dest.branch() |
|
610 | samebranch = root.branch() == dest.branch() | |
611 | if not collapse and samebranch and root in dest.children(): |
|
611 | if not collapse and samebranch and root in dest.children(): | |
612 | repo.ui.debug('source is a child of destination\n') |
|
612 | repo.ui.debug('source is a child of destination\n') | |
613 | return None |
|
613 | return None | |
614 |
|
614 | |||
615 |
repo.ui.debug('rebase onto %d starting from % |
|
615 | repo.ui.debug('rebase onto %d starting from %s\n' % (dest, roots)) | |
616 |
state |
|
616 | state.update(dict.fromkeys(rebaseset, nullrev)) | |
617 | # Rebase tries to turn <dest> into a parent of <root> while |
|
617 | # Rebase tries to turn <dest> into a parent of <root> while | |
618 | # preserving the number of parents of rebased changesets: |
|
618 | # preserving the number of parents of rebased changesets: | |
619 | # |
|
619 | # | |
620 | # - A changeset with a single parent will always be rebased as a |
|
620 | # - A changeset with a single parent will always be rebased as a | |
621 | # changeset with a single parent. |
|
621 | # changeset with a single parent. | |
622 | # |
|
622 | # | |
623 | # - A merge will be rebased as merge unless its parents are both |
|
623 | # - A merge will be rebased as merge unless its parents are both | |
624 | # ancestors of <dest> or are themselves in the rebased set and |
|
624 | # ancestors of <dest> or are themselves in the rebased set and | |
625 | # pruned while rebased. |
|
625 | # pruned while rebased. | |
626 | # |
|
626 | # | |
627 | # If one parent of <root> is an ancestor of <dest>, the rebased |
|
627 | # If one parent of <root> is an ancestor of <dest>, the rebased | |
628 | # version of this parent will be <dest>. This is always true with |
|
628 | # version of this parent will be <dest>. This is always true with | |
629 | # --base option. |
|
629 | # --base option. | |
630 | # |
|
630 | # | |
631 | # Otherwise, we need to *replace* the original parents with |
|
631 | # Otherwise, we need to *replace* the original parents with | |
632 | # <dest>. This "detaches" the rebased set from its former location |
|
632 | # <dest>. This "detaches" the rebased set from its former location | |
633 | # and rebases it onto <dest>. Changes introduced by ancestors of |
|
633 | # and rebases it onto <dest>. Changes introduced by ancestors of | |
634 | # <root> not common with <dest> (the detachset, marked as |
|
634 | # <root> not common with <dest> (the detachset, marked as | |
635 | # nullmerge) are "removed" from the rebased changesets. |
|
635 | # nullmerge) are "removed" from the rebased changesets. | |
636 | # |
|
636 | # | |
637 | # - If <root> has a single parent, set it to <dest>. |
|
637 | # - If <root> has a single parent, set it to <dest>. | |
638 | # |
|
638 | # | |
639 | # - If <root> is a merge, we cannot decide which parent to |
|
639 | # - If <root> is a merge, we cannot decide which parent to | |
640 | # replace, the rebase operation is not clearly defined. |
|
640 | # replace, the rebase operation is not clearly defined. | |
641 | # |
|
641 | # | |
642 | # The table below sums up this behavior: |
|
642 | # The table below sums up this behavior: | |
643 | # |
|
643 | # | |
644 |
# + |
|
644 | # +------------------+----------------------+-------------------------+ | |
645 |
|
|
645 | # | | one parent | merge | | |
646 |
# + |
|
646 | # +------------------+----------------------+-------------------------+ | |
647 |
# | parent in |
|
647 | # | parent in | new parent is <dest> | parents in ::<dest> are | | |
648 |
|
|
648 | # | ::<dest> | | remapped to <dest> | | |
649 |
# + |
|
649 | # +------------------+----------------------+-------------------------+ | |
650 |
# | unrelated source |
|
650 | # | unrelated source | new parent is <dest> | ambiguous, abort | | |
651 |
# + |
|
651 | # +------------------+----------------------+-------------------------+ | |
652 | # |
|
652 | # | |
653 | # The actual abort is handled by `defineparents` |
|
653 | # The actual abort is handled by `defineparents` | |
654 | if len(root.parents()) <= 1: |
|
654 | if len(root.parents()) <= 1: | |
655 | # ancestors of <root> not ancestors of <dest> |
|
655 | # ancestors of <root> not ancestors of <dest> | |
656 |
detachset |
|
656 | detachset.update(repo.changelog.findmissingrevs([commonbase.rev()], | |
657 | [root.rev()]) |
|
657 | [root.rev()])) | |
658 | state.update(dict.fromkeys(detachset, nullmerge)) |
|
658 | for r in detachset: | |
659 | # detachset can have root, and we definitely want to rebase that |
|
659 | if r not in state: | |
660 |
state[r |
|
660 | state[r] = nullmerge | |
661 | return repo['.'].rev(), dest.rev(), state |
|
661 | return repo['.'].rev(), dest.rev(), state | |
662 |
|
662 | |||
663 | def clearrebased(ui, repo, state, collapsedas=None): |
|
663 | def clearrebased(ui, repo, state, collapsedas=None): | |
@@ -677,12 +677,16 b' def clearrebased(ui, repo, state, collap' | |||||
677 | else: |
|
677 | else: | |
678 | rebased = [rev for rev in state if state[rev] != nullmerge] |
|
678 | rebased = [rev for rev in state if state[rev] != nullmerge] | |
679 | if rebased: |
|
679 | if rebased: | |
680 | if set(repo.changelog.descendants([min(rebased)])) - set(state): |
|
680 | stripped = [] | |
681 | ui.warn(_("warning: new changesets detected " |
|
681 | for root in repo.set('roots(%ld)', rebased): | |
682 | "on source branch, not stripping\n")) |
|
682 | if set(repo.changelog.descendants([root.rev()])) - set(state): | |
683 | else: |
|
683 | ui.warn(_("warning: new changesets detected " | |
|
684 | "on source branch, not stripping\n")) | |||
|
685 | else: | |||
|
686 | stripped.append(root.node()) | |||
|
687 | if stripped: | |||
684 | # backup the old csets by default |
|
688 | # backup the old csets by default | |
685 |
repair.strip(ui, repo, |
|
689 | repair.strip(ui, repo, stripped, "all") | |
686 |
|
690 | |||
687 |
|
691 | |||
688 | def pullrebase(orig, ui, repo, *args, **opts): |
|
692 | def pullrebase(orig, ui, repo, *args, **opts): |
@@ -306,3 +306,26 b' Test that rewriting leaving instability ' | |||||
306 |
|
306 | |||
307 |
|
307 | |||
308 |
|
308 | |||
|
309 | Test multiple root handling | |||
|
310 | ------------------------------------ | |||
|
311 | ||||
|
312 | $ hg rebase --dest 4 --rev '7+11+9' | |||
|
313 | $ hg log -G | |||
|
314 | @ 14:00891d85fcfc C | |||
|
315 | | | |||
|
316 | | o 13:102b4c1d889b D | |||
|
317 | |/ | |||
|
318 | | o 12:bfe264faf697 H | |||
|
319 | |/ | |||
|
320 | | o 10:7c6027df6a99 B | |||
|
321 | | | | |||
|
322 | | x 7:02de42196ebe H | |||
|
323 | | | | |||
|
324 | +---o 6:eea13746799a G | |||
|
325 | | |/ | |||
|
326 | | o 5:24b6387c8c8c F | |||
|
327 | | | | |||
|
328 | o | 4:9520eea781bc E | |||
|
329 | |/ | |||
|
330 | o 0:cd010b8cd998 A | |||
|
331 |
@@ -542,6 +542,108 b' We would expect heads are I, F if it was' | |||||
542 | $ hg clone -q -u . ah ah6 |
|
542 | $ hg clone -q -u . ah ah6 | |
543 | $ cd ah6 |
|
543 | $ cd ah6 | |
544 | $ hg rebase -r '(4+6)::' -d 1 |
|
544 | $ hg rebase -r '(4+6)::' -d 1 | |
545 | abort: can't rebase multiple roots |
|
545 | saved backup bundle to $TESTTMP/ah6/.hg/strip-backup/3d8a618087a7-backup.hg (glob) | |
546 | [255] |
|
546 | $ hg tglog | |
|
547 | @ 8: 'I' | |||
|
548 | | | |||
|
549 | o 7: 'H' | |||
|
550 | | | |||
|
551 | o 6: 'G' | |||
|
552 | | | |||
|
553 | | o 5: 'F' | |||
|
554 | | | | |||
|
555 | | o 4: 'E' | |||
|
556 | |/ | |||
|
557 | | o 3: 'D' | |||
|
558 | | | | |||
|
559 | | o 2: 'C' | |||
|
560 | | | | |||
|
561 | o | 1: 'B' | |||
|
562 | |/ | |||
|
563 | o 0: 'A' | |||
|
564 | ||||
547 | $ cd .. |
|
565 | $ cd .. | |
|
566 | ||||
|
567 | More complexe rebase with multiple roots | |||
|
568 | each root have a different common ancestor with the destination and this is a detach | |||
|
569 | ||||
|
570 | (setup) | |||
|
571 | ||||
|
572 | $ hg clone -q -u . a a8 | |||
|
573 | $ cd a8 | |||
|
574 | $ echo I > I | |||
|
575 | $ hg add I | |||
|
576 | $ hg commit -m I | |||
|
577 | $ hg up 4 | |||
|
578 | 1 files updated, 0 files merged, 3 files removed, 0 files unresolved | |||
|
579 | $ echo I > J | |||
|
580 | $ hg add J | |||
|
581 | $ hg commit -m J | |||
|
582 | created new head | |||
|
583 | $ echo I > K | |||
|
584 | $ hg add K | |||
|
585 | $ hg commit -m K | |||
|
586 | $ hg tglog | |||
|
587 | @ 10: 'K' | |||
|
588 | | | |||
|
589 | o 9: 'J' | |||
|
590 | | | |||
|
591 | | o 8: 'I' | |||
|
592 | | | | |||
|
593 | | o 7: 'H' | |||
|
594 | | | | |||
|
595 | +---o 6: 'G' | |||
|
596 | | |/ | |||
|
597 | | o 5: 'F' | |||
|
598 | | | | |||
|
599 | o | 4: 'E' | |||
|
600 | |/ | |||
|
601 | | o 3: 'D' | |||
|
602 | | | | |||
|
603 | | o 2: 'C' | |||
|
604 | | | | |||
|
605 | | o 1: 'B' | |||
|
606 | |/ | |||
|
607 | o 0: 'A' | |||
|
608 | ||||
|
609 | (actual test) | |||
|
610 | ||||
|
611 | $ hg rebase --dest 'desc(G)' --rev 'desc(K) + desc(I)' | |||
|
612 | saved backup bundle to $TESTTMP/a8/.hg/strip-backup/23a4ace37988-backup.hg (glob) | |||
|
613 | $ hg log --rev 'children(desc(G))' | |||
|
614 | changeset: 9:adb617877056 | |||
|
615 | parent: 6:eea13746799a | |||
|
616 | user: test | |||
|
617 | date: Thu Jan 01 00:00:00 1970 +0000 | |||
|
618 | summary: I | |||
|
619 | ||||
|
620 | changeset: 10:882431a34a0e | |||
|
621 | tag: tip | |||
|
622 | parent: 6:eea13746799a | |||
|
623 | user: test | |||
|
624 | date: Thu Jan 01 00:00:00 1970 +0000 | |||
|
625 | summary: K | |||
|
626 | ||||
|
627 | $ hg tglog | |||
|
628 | @ 10: 'K' | |||
|
629 | | | |||
|
630 | | o 9: 'I' | |||
|
631 | |/ | |||
|
632 | | o 8: 'J' | |||
|
633 | | | | |||
|
634 | | | o 7: 'H' | |||
|
635 | | | | | |||
|
636 | o---+ 6: 'G' | |||
|
637 | |/ / | |||
|
638 | | o 5: 'F' | |||
|
639 | | | | |||
|
640 | o | 4: 'E' | |||
|
641 | |/ | |||
|
642 | | o 3: 'D' | |||
|
643 | | | | |||
|
644 | | o 2: 'C' | |||
|
645 | | | | |||
|
646 | | o 1: 'B' | |||
|
647 | |/ | |||
|
648 | o 0: 'A' | |||
|
649 |
General Comments 0
You need to be logged in to leave comments.
Login now