163 _('detach requires a revision to be specified')) |
163 _('detach requires a revision to be specified')) |
164 if basef: |
164 if basef: |
165 raise util.Abort(_('cannot specify a base with detach')) |
165 raise util.Abort(_('cannot specify a base with detach')) |
166 |
166 |
167 cmdutil.bailifchanged(repo) |
167 cmdutil.bailifchanged(repo) |
168 result = buildstate(repo, destf, srcf, basef, detachf) |
168 |
|
169 if not destf: |
|
170 # Destination defaults to the latest revision in the current branch |
|
171 branch = repo[None].branch() |
|
172 dest = repo[branch] |
|
173 else: |
|
174 dest = repo[destf] |
|
175 |
|
176 if srcf: |
|
177 revsetargs = ('(%s)::', srcf) |
|
178 else: |
|
179 base = basef or '.' |
|
180 revsetargs = ('(children(ancestor(%s, %d)) and ::(%s))::', |
|
181 base, dest, base) |
|
182 |
|
183 rebaseset = [c.rev() for c in repo.set(*revsetargs)] |
|
184 if rebaseset: |
|
185 result = buildstate(repo, dest, rebaseset, detachf) |
|
186 else: |
|
187 repo.ui.debug(_('base is ancestor of destination')) |
|
188 result = None |
169 if not result: |
189 if not result: |
170 # Empty state built, nothing to rebase |
190 # Empty state built, nothing to rebase |
171 ui.status(_('nothing to rebase\n')) |
191 ui.status(_('nothing to rebase\n')) |
172 return 1 |
192 return 1 |
173 else: |
193 else: |
505 repair.strip(repo.ui, repo, repo[strippoint].node()) |
525 repair.strip(repo.ui, repo, repo[strippoint].node()) |
506 clearstatus(repo) |
526 clearstatus(repo) |
507 repo.ui.warn(_('rebase aborted\n')) |
527 repo.ui.warn(_('rebase aborted\n')) |
508 return 0 |
528 return 0 |
509 |
529 |
510 def buildstate(repo, dest, src, base, detach): |
530 def buildstate(repo, dest, rebaseset, detach): |
511 'Define which revisions are going to be rebased and where' |
531 '''Define which revisions are going to be rebased and where |
512 targetancestors = set() |
532 |
513 detachset = set() |
533 repo: repo |
514 |
534 dest: context |
515 if not dest: |
535 rebaseset: set of rev |
516 # Destination defaults to the latest revision in the current branch |
536 detach: boolean''' |
517 branch = repo[None].branch() |
|
518 dest = repo[branch].rev() |
|
519 else: |
|
520 dest = repo[dest].rev() |
|
521 |
537 |
522 # This check isn't strictly necessary, since mq detects commits over an |
538 # This check isn't strictly necessary, since mq detects commits over an |
523 # applied patch. But it prevents messing up the working directory when |
539 # applied patch. But it prevents messing up the working directory when |
524 # a partially completed rebase is blocked by mq. |
540 # a partially completed rebase is blocked by mq. |
525 if 'qtip' in repo.tags() and (repo[dest].node() in |
541 if 'qtip' in repo.tags() and (dest.node() in |
526 [s.node for s in repo.mq.applied]): |
542 [s.node for s in repo.mq.applied]): |
527 raise util.Abort(_('cannot rebase onto an applied mq patch')) |
543 raise util.Abort(_('cannot rebase onto an applied mq patch')) |
528 |
544 |
529 if src: |
545 detachset = set() |
530 commonbase = repo[src].ancestor(repo[dest]) |
546 roots = list(repo.set('roots(%ld)', rebaseset)) |
531 if commonbase == repo[src]: |
547 if not roots: |
532 raise util.Abort(_('source is ancestor of destination')) |
548 raise util.Abort( _('no matching revisions')) |
533 if commonbase == repo[dest]: |
549 if len(roots) > 1: |
534 samebranch = repo[src].branch() == repo[dest].branch() |
550 raise util.Abort( _("can't rebase multiple roots")) |
535 if samebranch and repo[src] in repo[dest].children(): |
551 root = roots[0] |
536 raise util.Abort(_('source is a child of destination')) |
552 |
537 # rebase on ancestor, force detach |
553 commonbase = root.ancestor(dest) |
538 detach = True |
554 if commonbase == root: |
539 source = repo[src].rev() |
555 raise util.Abort(_('source is ancestor of destination')) |
540 if detach: |
556 if commonbase == dest: |
541 # We need to keep track of source's ancestors up to the common base |
557 samebranch = root.branch() == dest.branch() |
542 srcancestors = set(repo.changelog.ancestors(source)) |
558 if samebranch and root in dest.children(): |
543 baseancestors = set(repo.changelog.ancestors(commonbase.rev())) |
559 repo.ui.debug(_('source is a child of destination')) |
544 detachset = srcancestors - baseancestors |
560 return None |
545 detachset.discard(commonbase.rev()) |
561 # rebase on ancestor, force detach |
546 else: |
562 detach = True |
547 if base: |
563 if detach: |
548 cwd = repo[base].rev() |
564 detachset = [c.rev() for c in repo.set('::%d - ::%d - %d', |
549 else: |
565 root, commonbase, root)] |
550 cwd = repo['.'].rev() |
566 |
551 |
567 repo.ui.debug('rebase onto %d starting from %d\n' % (dest, root)) |
552 if cwd == dest: |
568 state = dict.fromkeys(rebaseset, nullrev) |
553 repo.ui.debug('source and destination are the same\n') |
|
554 return None |
|
555 |
|
556 targetancestors = set(repo.changelog.ancestors(dest)) |
|
557 if cwd in targetancestors: |
|
558 repo.ui.debug('source is ancestor of destination\n') |
|
559 return None |
|
560 |
|
561 cwdancestors = set(repo.changelog.ancestors(cwd)) |
|
562 if dest in cwdancestors: |
|
563 repo.ui.debug('source is descendant of destination\n') |
|
564 return None |
|
565 |
|
566 cwdancestors.add(cwd) |
|
567 rebasingbranch = cwdancestors - targetancestors |
|
568 source = min(rebasingbranch) |
|
569 |
|
570 repo.ui.debug('rebase onto %d starting from %d\n' % (dest, source)) |
|
571 state = dict.fromkeys(repo.changelog.descendants(source), nullrev) |
|
572 state.update(dict.fromkeys(detachset, nullmerge)) |
569 state.update(dict.fromkeys(detachset, nullmerge)) |
573 state[source] = nullrev |
570 return repo['.'].rev(), dest.rev(), state |
574 return repo['.'].rev(), repo[dest].rev(), state |
|
575 |
571 |
576 def pullrebase(orig, ui, repo, *args, **opts): |
572 def pullrebase(orig, ui, repo, *args, **opts): |
577 'Call rebase after pull if the latter has been invoked with --rebase' |
573 'Call rebase after pull if the latter has been invoked with --rebase' |
578 if opts.get('rebase'): |
574 if opts.get('rebase'): |
579 if opts.get('update'): |
575 if opts.get('update'): |