359 dest = self.repo[destrev] |
359 dest = self.repo[destrev] |
360 if dest.closesbranch() and not self.keepbranchesf: |
360 if dest.closesbranch() and not self.keepbranchesf: |
361 self.ui.status(_('reopening closed branch head %s\n') % dest) |
361 self.ui.status(_('reopening closed branch head %s\n') % dest) |
362 |
362 |
363 def _performrebase(self, tr): |
363 def _performrebase(self, tr): |
364 repo, ui, opts = self.repo, self.ui, self.opts |
364 repo, ui = self.repo, self.ui |
365 if self.keepbranchesf: |
365 if self.keepbranchesf: |
366 # insert _savebranch at the start of extrafns so if |
366 # insert _savebranch at the start of extrafns so if |
367 # there's a user-provided extrafn it can clobber branch if |
367 # there's a user-provided extrafn it can clobber branch if |
368 # desired |
368 # desired |
369 self.extrafns.insert(0, _savebranch) |
369 self.extrafns.insert(0, _savebranch) |
382 |
382 |
383 # Store the state before we begin so users can run 'hg rebase --abort' |
383 # Store the state before we begin so users can run 'hg rebase --abort' |
384 # if we fail before the transaction closes. |
384 # if we fail before the transaction closes. |
385 self.storestatus() |
385 self.storestatus() |
386 |
386 |
387 sortedrevs = repo.revs('sort(%ld, -topo)', self.state) |
|
388 cands = [k for k, v in self.state.iteritems() if v == revtodo] |
387 cands = [k for k, v in self.state.iteritems() if v == revtodo] |
389 total = len(cands) |
388 total = len(cands) |
390 pos = 0 |
389 pos = 0 |
|
390 for subset in sortsource(self.destmap): |
|
391 pos = self._performrebasesubset(tr, subset, pos, total) |
|
392 ui.progress(_('rebasing'), None) |
|
393 ui.note(_('rebase merging completed\n')) |
|
394 |
|
395 def _performrebasesubset(self, tr, subset, pos, total): |
|
396 repo, ui, opts = self.repo, self.ui, self.opts |
|
397 sortedrevs = repo.revs('sort(%ld, -topo)', subset) |
391 for rev in sortedrevs: |
398 for rev in sortedrevs: |
392 dest = self.destmap[rev] |
399 dest = self.destmap[rev] |
393 ctx = repo[rev] |
400 ctx = repo[rev] |
394 desc = _ctxdesc(ctx) |
401 desc = _ctxdesc(ctx) |
395 if self.state[rev] == rev: |
402 if self.state[rev] == rev: |
447 self.state[rev] = p1 |
454 self.state[rev] = p1 |
448 ui.debug('next revision set to %s\n' % p1) |
455 ui.debug('next revision set to %s\n' % p1) |
449 else: |
456 else: |
450 ui.status(_('already rebased %s as %s\n') % |
457 ui.status(_('already rebased %s as %s\n') % |
451 (desc, repo[self.state[rev]])) |
458 (desc, repo[self.state[rev]])) |
452 |
459 return pos |
453 ui.progress(_('rebasing'), None) |
|
454 ui.note(_('rebase merging completed\n')) |
|
455 |
460 |
456 def _finishrebase(self): |
461 def _finishrebase(self): |
457 repo, ui, opts = self.repo, self.ui, self.opts |
462 repo, ui, opts = self.repo, self.ui, self.opts |
458 if self.collapsef and not self.keepopen: |
463 if self.collapsef and not self.keepopen: |
459 p1, p2, _base = defineparents(repo, min(self.state), self.destmap, |
464 p1, p2, _base = defineparents(repo, min(self.state), self.destmap, |
967 | C D | |
972 | C D | |
968 | |/ | |
973 | |/ | |
969 | B | ... |
974 | B | ... |
970 |/ |/ |
975 |/ |/ |
971 A A |
976 A A |
|
977 |
|
978 Besides, adjust dest according to existing rebase information. For example, |
|
979 |
|
980 B C D B needs to be rebased on top of C, C needs to be rebased on top |
|
981 \|/ of D. We will rebase C first. |
|
982 A |
|
983 |
|
984 C' After rebasing C, when considering B's destination, use C' |
|
985 | instead of the original C. |
|
986 B D |
|
987 \ / |
|
988 A |
972 """ |
989 """ |
973 # pick already rebased revs with same dest from state as interesting source |
990 # pick already rebased revs with same dest from state as interesting source |
974 dest = destmap[rev] |
991 dest = destmap[rev] |
975 source = [s for s, d in state.items() if d > 0 and destmap[s] == dest] |
992 source = [s for s, d in state.items() if d > 0 and destmap[s] == dest] |
976 |
993 |
979 adjusted = dest |
996 adjusted = dest |
980 if prev != nullrev: |
997 if prev != nullrev: |
981 candidate = repo.revs('max(%ld and (::%d))', source, prev).first() |
998 candidate = repo.revs('max(%ld and (::%d))', source, prev).first() |
982 if candidate is not None: |
999 if candidate is not None: |
983 adjusted = state[candidate] |
1000 adjusted = state[candidate] |
|
1001 if adjusted == dest and dest in state: |
|
1002 adjusted = state[dest] |
|
1003 if adjusted == revtodo: |
|
1004 # sortsource should produce an order that makes this impossible |
|
1005 raise error.ProgrammingError( |
|
1006 'rev %d should be rebased already at this time' % dest) |
984 result.append(adjusted) |
1007 result.append(adjusted) |
985 return result |
1008 return result |
986 |
1009 |
987 def _checkobsrebase(repo, ui, rebaseobsrevs, rebaseobsskipped): |
1010 def _checkobsrebase(repo, ui, rebaseobsrevs, rebaseobsskipped): |
988 """ |
1011 """ |
1126 if set(newps) == set(oldps) and dest not in newps: |
1149 if set(newps) == set(oldps) and dest not in newps: |
1127 raise error.Abort(_('cannot rebase %d:%s without ' |
1150 raise error.Abort(_('cannot rebase %d:%s without ' |
1128 'moving at least one of its parents') |
1151 'moving at least one of its parents') |
1129 % (rev, repo[rev])) |
1152 % (rev, repo[rev])) |
1130 |
1153 |
|
1154 # Source should not be ancestor of dest. The check here guarantees it's |
|
1155 # impossible. With multi-dest, the initial check does not cover complex |
|
1156 # cases since we don't have abstractions to dry-run rebase cheaply. |
|
1157 if any(p != nullrev and isancestor(rev, p) for p in newps): |
|
1158 raise error.Abort(_('source is ancestor of destination')) |
|
1159 |
1131 # "rebasenode" updates to new p1, use the corresponding merge base. |
1160 # "rebasenode" updates to new p1, use the corresponding merge base. |
1132 if bases[0] != nullrev: |
1161 if bases[0] != nullrev: |
1133 base = bases[0] |
1162 base = bases[0] |
1134 else: |
1163 else: |
1135 base = None |
1164 base = None |
1352 clearstatus(repo) |
1381 clearstatus(repo) |
1353 clearcollapsemsg(repo) |
1382 clearcollapsemsg(repo) |
1354 repo.ui.warn(_('rebase aborted\n')) |
1383 repo.ui.warn(_('rebase aborted\n')) |
1355 return 0 |
1384 return 0 |
1356 |
1385 |
|
1386 def sortsource(destmap): |
|
1387 """yield source revisions in an order that we only rebase things once |
|
1388 |
|
1389 If source and destination overlaps, we should filter out revisions |
|
1390 depending on other revisions which hasn't been rebased yet. |
|
1391 |
|
1392 Yield a sorted list of revisions each time. |
|
1393 |
|
1394 For example, when rebasing A to B, B to C. This function yields [B], then |
|
1395 [A], indicating B needs to be rebased first. |
|
1396 |
|
1397 Raise if there is a cycle so the rebase is impossible. |
|
1398 """ |
|
1399 srcset = set(destmap) |
|
1400 while srcset: |
|
1401 srclist = sorted(srcset) |
|
1402 result = [] |
|
1403 for r in srclist: |
|
1404 if destmap[r] not in srcset: |
|
1405 result.append(r) |
|
1406 if not result: |
|
1407 raise error.Abort(_('source and destination form a cycle')) |
|
1408 srcset -= set(result) |
|
1409 yield result |
|
1410 |
1357 def buildstate(repo, destmap, collapse, obsoletenotrebased): |
1411 def buildstate(repo, destmap, collapse, obsoletenotrebased): |
1358 '''Define which revisions are going to be rebased and where |
1412 '''Define which revisions are going to be rebased and where |
1359 |
1413 |
1360 repo: repo |
1414 repo: repo |
1361 destmap: {srcrev: destrev} |
1415 destmap: {srcrev: destrev} |
1370 if 'qtip' in repo.tags(): |
1424 if 'qtip' in repo.tags(): |
1371 mqapplied = set(repo[s.node].rev() for s in repo.mq.applied) |
1425 mqapplied = set(repo[s.node].rev() for s in repo.mq.applied) |
1372 if set(destmap.values()) & mqapplied: |
1426 if set(destmap.values()) & mqapplied: |
1373 raise error.Abort(_('cannot rebase onto an applied mq patch')) |
1427 raise error.Abort(_('cannot rebase onto an applied mq patch')) |
1374 |
1428 |
1375 roots = list(repo.set('roots(%ld)', rebaseset)) |
1429 # Get "cycle" error early by exhausting the generator. |
|
1430 sortedsrc = list(sortsource(destmap)) # a list of sorted revs |
|
1431 if not sortedsrc: |
|
1432 raise error.Abort(_('no matching revisions')) |
|
1433 |
|
1434 # Only check the first batch of revisions to rebase not depending on other |
|
1435 # rebaseset. This means "source is ancestor of destination" for the second |
|
1436 # (and following) batches of revisions are not checked here. We rely on |
|
1437 # "defineparents" to do that check. |
|
1438 roots = list(repo.set('roots(%ld)', sortedsrc[0])) |
1376 if not roots: |
1439 if not roots: |
1377 raise error.Abort(_('no matching revisions')) |
1440 raise error.Abort(_('no matching revisions')) |
1378 roots.sort() |
1441 roots.sort() |
1379 state = dict.fromkeys(rebaseset, revtodo) |
1442 state = dict.fromkeys(rebaseset, revtodo) |
1380 emptyrebase = True |
1443 emptyrebase = (len(sortedsrc) == 1) |
1381 for root in roots: |
1444 for root in roots: |
1382 dest = repo[destmap[root.rev()]] |
1445 dest = repo[destmap[root.rev()]] |
1383 commonbase = root.ancestor(dest) |
1446 commonbase = root.ancestor(dest) |
1384 if commonbase == root: |
1447 if commonbase == root: |
1385 raise error.Abort(_('source is ancestor of destination')) |
1448 raise error.Abort(_('source is ancestor of destination')) |