diff --git a/hgext/rebase.py b/hgext/rebase.py
--- a/hgext/rebase.py
+++ b/hgext/rebase.py
@@ -48,8 +48,7 @@ testedwith = 'internal'
      _('read collapse commit message from file'), _('FILE')),
     ('', 'keep', False, _('keep original changesets')),
     ('', 'keepbranches', False, _('keep original branch names')),
-    ('D', 'detach', False, _('force detaching of source from its original '
-                            'branch')),
+    ('D', 'detach', False, _('(DEPRECATED)')),
     ('t', 'tool', '', _('specify merge tool')),
     ('c', 'continue', False, _('continue an interrupted rebase')),
     ('a', 'abort', False, _('abort an interrupted rebase'))] +
@@ -131,7 +130,6 @@ def rebase(ui, repo, **opts):
         extrafn = opts.get('extrafn') # internal, used by e.g. hgsubversion
         keepf = opts.get('keep', False)
         keepbranchesf = opts.get('keepbranches', False)
-        detachf = opts.get('detach', False)
         # keepopen is not meant for use on the command line, but by
         # other extensions
         keepopen = opts.get('keepopen', False)
@@ -146,8 +144,6 @@ def rebase(ui, repo, **opts):
             if collapsef:
                 raise util.Abort(
                     _('cannot use collapse with continue or abort'))
-            if detachf:
-                raise util.Abort(_('cannot use detach with continue or abort'))
             if srcf or basef or destf:
                 raise util.Abort(
                     _('abort and continue do not allow specifying revisions'))
@@ -168,12 +164,6 @@ def rebase(ui, repo, **opts):
             if revf and srcf:
                 raise util.Abort(_('cannot specify both a '
                                    'revision and a source'))
-            if detachf:
-                if not (srcf or revf):
-                    raise util.Abort(
-                        _('detach requires a revision to be specified'))
-                if basef:
-                    raise util.Abort(_('cannot specify a base with detach'))
 
             cmdutil.bailifchanged(repo)
 
@@ -215,7 +205,7 @@ def rebase(ui, repo, **opts):
                                  % repo[root],
                                  hint=_('see hg help phases for details'))
             else:
-                result = buildstate(repo, dest, rebaseset, detachf, collapsef)
+                result = buildstate(repo, dest, rebaseset, collapsef)
 
             if not result:
                 # Empty state built, nothing to rebase
@@ -592,13 +582,13 @@ def abort(repo, originalwd, target, stat
         repo.ui.warn(_('rebase aborted\n'))
         return 0
 
-def buildstate(repo, dest, rebaseset, detach, collapse):
+def buildstate(repo, dest, rebaseset, collapse):
     '''Define which revisions are going to be rebased and where
 
     repo: repo
     dest: context
     rebaseset: set of rev
-    detach: boolean'''
+    '''
 
     # This check isn't strictly necessary, since mq detects commits over an
     # applied patch. But it prevents messing up the working directory when
@@ -607,7 +597,6 @@ def buildstate(repo, dest, rebaseset, de
                             [s.node for s in repo.mq.applied]):
         raise util.Abort(_('cannot rebase onto an applied mq patch'))
 
-    detachset = set()
     roots = list(repo.set('roots(%ld)', rebaseset))
     if not roots:
         raise util.Abort(_('no matching revisions'))
@@ -623,14 +612,50 @@ def buildstate(repo, dest, rebaseset, de
         if not collapse and samebranch and root in dest.children():
             repo.ui.debug('source is a child of destination\n')
             return None
-        # rebase on ancestor, force detach
-        detach = True
-    if detach:
-        detachset = repo.revs('::%d - ::%d - %d', root, commonbase, root)
 
     repo.ui.debug('rebase onto %d starting from %d\n' % (dest, root))
     state = dict.fromkeys(rebaseset, nullrev)
-    state.update(dict.fromkeys(detachset, nullmerge))
+    # Rebase tries to turn <dest> into a parent of <root> while
+    # preserving the number of parents of rebased changesets:
+    #
+    # - A changeset with a single parent will always be rebased as a
+    #   changeset with a single parent.
+    #
+    # - A merge will be rebased as merge unless its parents are both
+    #   ancestors of <dest> or are themselves in the rebased set and
+    #   pruned while rebased.
+    #
+    # If one parent of <root> is an ancestor of <dest>, the rebased
+    # version of this parent will be <dest>. This is always true with
+    # --base option.
+    #
+    # Otherwise, we need to *replace* the original parents with
+    # <dest>. This "detaches" the rebased set from its former location
+    # and rebases it onto <dest>. Changes introduced by ancestors of
+    # <root> not common with <dest> (the detachset, marked as
+    # nullmerge) are "removed" from the rebased changesets.
+    #
+    # - If <root> has a single parent, set it to <dest>.
+    #
+    # - If <root> is a merge, we cannot decide which parent to
+    #   replace, the rebase operation is not clearly defined.
+    #
+    # The table below sums up this behavior:
+    #
+    # +--------------------+----------------------+-------------------------+
+    # |                    |     one parent       |  merge                  |
+    # +--------------------+----------------------+-------------------------+
+    # | parent in ::<dest> | new parent is <dest> | parents in ::<dest> are |
+    # |                    |                      | remapped to <dest>      |
+    # +--------------------+----------------------+-------------------------+
+    # | unrelated source   | new parent is <dest> | ambiguous, abort        |
+    # +--------------------+----------------------+-------------------------+
+    #
+    # The actual abort is handled by `defineparents`
+    if len(root.parents()) <= 1:
+        # (strict) ancestors of <root> not ancestors of <dest>
+        detachset = repo.revs('::%d - ::%d - %d', root, commonbase, root)
+        state.update(dict.fromkeys(detachset, nullmerge))
     return repo['.'].rev(), dest.rev(), state
 
 def pullrebase(orig, ui, repo, *args, **opts):
diff --git a/tests/test-bookmarks-rebase.t b/tests/test-bookmarks-rebase.t
--- a/tests/test-bookmarks-rebase.t
+++ b/tests/test-bookmarks-rebase.t
@@ -39,11 +39,10 @@ rebase
   saved backup bundle to $TESTTMP/.hg/strip-backup/*-backup.hg (glob)
 
   $ hg log
-  changeset:   3:9163974d1cb5
+  changeset:   3:42e5ed2cdcf4
   bookmark:    two
   tag:         tip
   parent:      1:925d80f479bb
-  parent:      2:db815d6d32e6
   user:        test
   date:        Thu Jan 01 00:00:00 1970 +0000
   summary:     3
diff --git a/tests/test-rebase-bookmarks.t b/tests/test-rebase-bookmarks.t
--- a/tests/test-rebase-bookmarks.t
+++ b/tests/test-rebase-bookmarks.t
@@ -54,7 +54,7 @@ Move only rebased bookmarks
   $ cd a1
   $ hg up -q Z
 
-  $ hg rebase --detach -s Y -d 3
+  $ hg rebase -s Y -d 3
   saved backup bundle to $TESTTMP/a1/.hg/strip-backup/*-backup.hg (glob)
 
   $ hg tglog 
diff --git a/tests/test-rebase-cache.t b/tests/test-rebase-cache.t
--- a/tests/test-rebase-cache.t
+++ b/tests/test-rebase-cache.t
@@ -104,7 +104,7 @@ Rebase part of branch2 (5-6) onto branch
   2: 'B' branch1
   0: 'A' 
 
-  $ hg rebase --detach -s 5 -d 8
+  $ hg rebase -s 5 -d 8
   saved backup bundle to $TESTTMP/a1/.hg/strip-backup/*-backup.hg (glob)
 
   $ hg branches
@@ -165,7 +165,7 @@ Rebase head of branch3 (8) onto branch2 
   |/
   o  0: 'A'
   
-  $ hg rebase --detach -s 8 -d 6
+  $ hg rebase -s 8 -d 6
   saved backup bundle to $TESTTMP/a2/.hg/strip-backup/*-backup.hg (glob)
 
   $ hg branches
@@ -229,7 +229,7 @@ Rebase entire branch3 (7-8) onto branch2
   |/
   o  0: 'A'
   
-  $ hg rebase --detach -s 7 -d 6
+  $ hg rebase -s 7 -d 6
   saved backup bundle to $TESTTMP/a3/.hg/strip-backup/*-backup.hg (glob)
 
   $ hg branches
diff --git a/tests/test-rebase-collapse.t b/tests/test-rebase-collapse.t
--- a/tests/test-rebase-collapse.t
+++ b/tests/test-rebase-collapse.t
@@ -230,7 +230,7 @@ Rebase and collapse - more than one exte
 
 Rebase and collapse - E onto H:
 
-  $ hg rebase -s 4 --collapse
+  $ hg rebase -s 4 --collapse # root (4) is not a merge
   saved backup bundle to $TESTTMP/b1/.hg/strip-backup/*-backup.hg (glob)
 
   $ hg tglog
@@ -250,7 +250,6 @@ Rebase and collapse - E onto H:
   
   $ hg manifest
   A
-  B
   C
   D
   E
@@ -340,7 +339,7 @@ Rebase and collapse - E onto I:
   $ hg clone -q -u . c c1
   $ cd c1
 
-  $ hg rebase -s 4 --collapse
+  $ hg rebase -s 4 --collapse # root (4) is not a merge
   merging E
   saved backup bundle to $TESTTMP/c1/.hg/strip-backup/*-backup.hg (glob)
 
@@ -362,7 +361,6 @@ Rebase and collapse - E onto I:
   
   $ hg manifest
   A
-  B
   C
   D
   E
diff --git a/tests/test-rebase-detach.t b/tests/test-rebase-detach.t
--- a/tests/test-rebase-detach.t
+++ b/tests/test-rebase-detach.t
@@ -48,7 +48,7 @@ Rebasing D onto H detaching from C:
   o  0: 'A'
   
   $ hg phase --force --secret 3
-  $ hg rebase --detach -s 3 -d 7
+  $ hg rebase -s 3 -d 7
   saved backup bundle to $TESTTMP/a1/.hg/strip-backup/*-backup.hg (glob)
 
   $ hg log -G --template "{rev}:{phase} '{desc}' {branches}\n"
@@ -99,7 +99,7 @@ Rebasing C onto H detaching from B:
   |/
   o  0: 'A'
   
-  $ hg rebase --detach -s 2 -d 7
+  $ hg rebase -s 2 -d 7
   saved backup bundle to $TESTTMP/a2/.hg/strip-backup/*-backup.hg (glob)
 
   $ hg tglog
@@ -151,7 +151,7 @@ Rebasing B onto H using detach (same as 
   |/
   o  0: 'A'
   
-  $ hg rebase --detach -s 1 -d 7
+  $ hg rebase -s 1 -d 7
   saved backup bundle to $TESTTMP/a3/.hg/strip-backup/*-backup.hg (glob)
 
   $ hg tglog
@@ -205,7 +205,7 @@ Rebasing C onto H detaching from B and c
   |/
   o  0: 'A'
   
-  $ hg rebase --detach --collapse -s 2 -d 7
+  $ hg rebase --collapse -s 2 -d 7
   saved backup bundle to $TESTTMP/a4/.hg/strip-backup/*-backup.hg (glob)
 
   $ hg  log -G --template "{rev}:{phase} '{desc}' {branches}\n"
@@ -264,7 +264,7 @@ Rebasing across null as ancestor
   |/
   o  0: 'A'
   
-  $ hg rebase --detach -s 1 -d tip
+  $ hg rebase -s 1 -d tip
   saved backup bundle to $TESTTMP/a5/.hg/strip-backup/*-backup.hg (glob)
 
   $ hg tglog
@@ -325,7 +325,7 @@ Verify that target is not selected as ex
   $ echo "J" >> F
   $ hg ci -m "J"
 
-  $ hg rebase -s 8 -d 7 --collapse --detach --config ui.merge=internal:other
+  $ hg rebase -s 8 -d 7 --collapse --config ui.merge=internal:other
   remote changed E which local deleted
   use (c)hanged version or leave (d)eleted? c
   saved backup bundle to $TESTTMP/a6/.hg/strip-backup/*-backup.hg (glob)
@@ -370,7 +370,7 @@ Ensure --continue restores a correct sta
   $ hg ci -A -m 'H2'
   adding H
   $ hg phase --force --secret 8
-  $ hg rebase -s 8 -d 7 --detach --config ui.merge=internal:fail
+  $ hg rebase -s 8 -d 7 --config ui.merge=internal:fail
   merging H
   warning: conflicts during merge.
   merging H incomplete! (edit conflicts, then use 'hg resolve --mark')
diff --git a/tests/test-rebase-parameters.t b/tests/test-rebase-parameters.t
--- a/tests/test-rebase-parameters.t
+++ b/tests/test-rebase-parameters.t
@@ -199,19 +199,19 @@ Specify only source (from 2 onto 8):
   $ hg tglog
   @  8: 'D'
   |
-  o    7: 'C'
-  |\
-  | o  6: 'I'
-  | |
-  | o  5: 'H'
+  o  7: 'C'
+  |
+  o  6: 'I'
+  |
+  o  5: 'H'
+  |
+  | o  4: 'G'
+  |/|
+  o |  3: 'F'
   | |
-  | | o  4: 'G'
-  | |/|
-  | o |  3: 'F'
-  | | |
-  | | o  2: 'E'
-  | |/
-  o |  1: 'B'
+  | o  2: 'E'
+  |/
+  | o  1: 'B'
   |/
   o  0: 'A'
   
@@ -283,7 +283,7 @@ Specify source and dest (from 2 onto 7):
   $ hg clone -q -u . a a7
   $ cd a7
 
-  $ hg rebase --detach --source 2 --dest 7
+  $ hg rebase --source 2 --dest 7
   saved backup bundle to $TESTTMP/a7/.hg/strip-backup/*-backup.hg (glob)
 
   $ hg tglog
@@ -349,19 +349,19 @@ Specify only revs (from 2 onto 8)
   $ hg tglog
   @  8: 'D'
   |
-  o    7: 'C'
-  |\
-  | o  6: 'I'
-  | |
-  | o  5: 'H'
+  o  7: 'C'
+  |
+  o  6: 'I'
+  |
+  o  5: 'H'
+  |
+  | o  4: 'G'
+  |/|
+  o |  3: 'F'
   | |
-  | | o  4: 'G'
-  | |/|
-  | o |  3: 'F'
-  | | |
-  | | o  2: 'E'
-  | |/
-  o |  1: 'B'
+  | o  2: 'E'
+  |/
+  | o  1: 'B'
   |/
   o  0: 'A'
   
diff --git a/tests/test-rebase-scenario-global.t b/tests/test-rebase-scenario-global.t
--- a/tests/test-rebase-scenario-global.t
+++ b/tests/test-rebase-scenario-global.t
@@ -52,19 +52,19 @@ D onto H - simple rebase:
   saved backup bundle to $TESTTMP/a1/.hg/strip-backup/*-backup.hg (glob)
 
   $ hg tglog
-  @    7: 'D'
-  |\
-  | o  6: 'H'
+  @  7: 'D'
+  |
+  o  6: 'H'
+  |
+  | o  5: 'G'
+  |/|
+  o |  4: 'F'
   | |
-  | | o  5: 'G'
-  | |/|
-  | o |  4: 'F'
-  | | |
-  | | o  3: 'E'
-  | |/
-  o |  2: 'C'
+  | o  3: 'E'
+  |/
+  | o  2: 'C'
   | |
-  o |  1: 'B'
+  | o  1: 'B'
   |/
   o  0: 'A'
   
@@ -80,19 +80,19 @@ D onto F - intermediate point:
   saved backup bundle to $TESTTMP/a2/.hg/strip-backup/*-backup.hg (glob)
 
   $ hg tglog
-  @    7: 'D'
-  |\
-  | | o  6: 'H'
-  | |/
-  | | o  5: 'G'
-  | |/|
-  | o |  4: 'F'
-  | | |
-  | | o  3: 'E'
-  | |/
-  o |  2: 'C'
+  @  7: 'D'
+  |
+  | o  6: 'H'
+  |/
+  | o  5: 'G'
+  |/|
+  o |  4: 'F'
   | |
-  o |  1: 'B'
+  | o  3: 'E'
+  |/
+  | o  2: 'C'
+  | |
+  | o  1: 'B'
   |/
   o  0: 'A'
   
@@ -303,9 +303,9 @@ Source phase greater or equal to destina
   $ hg log --template "{phase}\n" -r 9
   secret
 Source phase lower than destination phase: new changeset get the phase of destination:
-  $ hg rebase -s7 -d9
-  saved backup bundle to $TESTTMP/a7/.hg/strip-backup/c9659aac0000-backup.hg (glob)
-  $ hg log --template "{phase}\n" -r 9
+  $ hg rebase -s8 -d9
+  saved backup bundle to $TESTTMP/a7/.hg/strip-backup/6d4f22462821-backup.hg
+  $ hg log --template "{phase}\n" -r 'rev(9)'
   secret
 
   $ cd ..
@@ -404,20 +404,20 @@ Base on have one descendant heads we ask
   |
   o  10: 'G'
   |
-  o    9: 'D'
-  |\
-  | | o  8: 'I'
-  | | |
-  | | o  7: 'H'
+  o  9: 'D'
+  |
+  | o  8: 'I'
+  | |
+  | o  7: 'H'
+  | |
+  | o  6: 'G'
+  | |
+  | | o  5: 'F'
   | | |
-  | | o  6: 'G'
-  | | |
-  | | | o  5: 'F'
-  | | | |
-  | | | o  4: 'E'
-  | | |/
-  | | o  3: 'D'
+  | | o  4: 'E'
   | |/
+  | o  3: 'D'
+  | |
   | o  2: 'C'
   | |
   o |  1: 'B'
@@ -441,20 +441,20 @@ rebase subset
   |
   o  10: 'G'
   |
-  o    9: 'D'
-  |\
-  | | o  8: 'I'
-  | | |
-  | | o  7: 'H'
+  o  9: 'D'
+  |
+  | o  8: 'I'
+  | |
+  | o  7: 'H'
+  | |
+  | o  6: 'G'
+  | |
+  | | o  5: 'F'
   | | |
-  | | o  6: 'G'
-  | | |
-  | | | o  5: 'F'
-  | | | |
-  | | | o  4: 'E'
-  | | |/
-  | | o  3: 'D'
+  | | o  4: 'E'
   | |/
+  | o  3: 'D'
+  | |
   | o  2: 'C'
   | |
   o |  1: 'B'
@@ -482,20 +482,20 @@ rebase subset with multiple head
   | |
   | o  10: 'E'
   |/
-  o    9: 'D'
-  |\
-  | | o  8: 'I'
-  | | |
-  | | o  7: 'H'
+  o  9: 'D'
+  |
+  | o  8: 'I'
+  | |
+  | o  7: 'H'
+  | |
+  | o  6: 'G'
+  | |
+  | | o  5: 'F'
   | | |
-  | | o  6: 'G'
-  | | |
-  | | | o  5: 'F'
-  | | | |
-  | | | o  4: 'E'
-  | | |/
-  | | o  3: 'D'
+  | | o  4: 'E'
   | |/
+  | o  3: 'D'
+  | |
   | o  2: 'C'
   | |
   o |  1: 'B'