58 # The following constants are used throughout the rebase module. The ordering of |
58 # The following constants are used throughout the rebase module. The ordering of |
59 # their values must be maintained. |
59 # their values must be maintained. |
60 |
60 |
61 # Indicates that a revision needs to be rebased |
61 # Indicates that a revision needs to be rebased |
62 revtodo = -1 |
62 revtodo = -1 |
63 nullmerge = -2 |
|
64 revignored = -3 |
|
65 |
63 |
66 # legacy revstates no longer needed in current code |
64 # legacy revstates no longer needed in current code |
67 # -4: revprecursor, -5: revpruned |
65 # -2: nullmerge, -3: revignored, -4: revprecursor, -5: revpruned |
68 legacystates = {'-4', '-5'} |
66 legacystates = {'-2', '-3', '-4', '-5'} |
69 |
67 |
70 cmdtable = {} |
68 cmdtable = {} |
71 command = registrar.command(cmdtable) |
69 command = registrar.command(cmdtable) |
72 # Note for extension authors: ONLY specify testedwith = 'ships-with-hg-core' for |
70 # Note for extension authors: ONLY specify testedwith = 'ships-with-hg-core' for |
73 # extensions which SHIP WITH MERCURIAL. Non-mainline extensions should |
71 # extensions which SHIP WITH MERCURIAL. Non-mainline extensions should |
231 activebookmark = l |
229 activebookmark = l |
232 else: |
230 else: |
233 oldrev, newrev = l.split(':') |
231 oldrev, newrev = l.split(':') |
234 if newrev in legacystates: |
232 if newrev in legacystates: |
235 continue |
233 continue |
236 if newrev in (str(nullmerge), str(revignored)): |
|
237 state[repo[oldrev].rev()] = int(newrev) |
|
238 elif newrev == nullid: |
234 elif newrev == nullid: |
239 state[repo[oldrev].rev()] = revtodo |
235 state[repo[oldrev].rev()] = revtodo |
240 # Legacy compat special case |
236 # Legacy compat special case |
241 else: |
237 else: |
242 state[repo[oldrev].rev()] = repo[newrev].rev() |
238 state[repo[oldrev].rev()] = repo[newrev].rev() |
437 ui.warn(_('note: rebase of %d:%s created no changes ' |
433 ui.warn(_('note: rebase of %d:%s created no changes ' |
438 'to commit\n') % (rev, ctx)) |
434 'to commit\n') % (rev, ctx)) |
439 self.skipped.add(rev) |
435 self.skipped.add(rev) |
440 self.state[rev] = p1 |
436 self.state[rev] = p1 |
441 ui.debug('next revision set to %s\n' % p1) |
437 ui.debug('next revision set to %s\n' % p1) |
442 elif self.state[rev] == nullmerge: |
|
443 pass |
|
444 elif self.state[rev] == revignored: |
|
445 pass |
|
446 else: |
438 else: |
447 ui.status(_('already rebased %s as %s\n') % |
439 ui.status(_('already rebased %s as %s\n') % |
448 (desc, repo[self.state[rev]])) |
440 (desc, repo[self.state[rev]])) |
449 |
441 |
450 ui.progress(_('rebasing'), None) |
442 ui.progress(_('rebasing'), None) |
461 commitmsg = self.collapsemsg |
453 commitmsg = self.collapsemsg |
462 else: |
454 else: |
463 commitmsg = 'Collapsed revision' |
455 commitmsg = 'Collapsed revision' |
464 for rebased in sorted(self.state): |
456 for rebased in sorted(self.state): |
465 if rebased not in self.skipped and\ |
457 if rebased not in self.skipped and\ |
466 self.state[rebased] > nullmerge: |
458 self.state[rebased] >= revtodo: |
467 commitmsg += '\n* %s' % repo[rebased].description() |
459 commitmsg += '\n* %s' % repo[rebased].description() |
468 editopt = True |
460 editopt = True |
469 editor = cmdutil.getcommiteditor(edit=editopt, editform=editform) |
461 editor = cmdutil.getcommiteditor(edit=editopt, editform=editform) |
470 revtoreuse = max(self.state) |
462 revtoreuse = max(self.state) |
471 |
463 |
482 if newnode is None: |
474 if newnode is None: |
483 newrev = self.dest |
475 newrev = self.dest |
484 else: |
476 else: |
485 newrev = repo[newnode].rev() |
477 newrev = repo[newnode].rev() |
486 for oldrev in self.state.iterkeys(): |
478 for oldrev in self.state.iterkeys(): |
487 if self.state[oldrev] > nullmerge: |
479 if self.state[oldrev] >= revtodo: |
488 self.state[oldrev] = newrev |
480 self.state[oldrev] = newrev |
489 |
481 |
490 if 'qtip' in repo.tags(): |
482 if 'qtip' in repo.tags(): |
491 updatemq(repo, self.state, self.skipped, **opts) |
483 updatemq(repo, self.state, self.skipped, **opts) |
492 |
484 |
1338 repo.ui.debug('source is a child of destination\n') |
1329 repo.ui.debug('source is a child of destination\n') |
1339 continue |
1330 continue |
1340 |
1331 |
1341 emptyrebase = False |
1332 emptyrebase = False |
1342 repo.ui.debug('rebase onto %s starting from %s\n' % (dest, root)) |
1333 repo.ui.debug('rebase onto %s starting from %s\n' % (dest, root)) |
1343 # Rebase tries to turn <dest> into a parent of <root> while |
|
1344 # preserving the number of parents of rebased changesets: |
|
1345 # |
|
1346 # - A changeset with a single parent will always be rebased as a |
|
1347 # changeset with a single parent. |
|
1348 # |
|
1349 # - A merge will be rebased as merge unless its parents are both |
|
1350 # ancestors of <dest> or are themselves in the rebased set and |
|
1351 # pruned while rebased. |
|
1352 # |
|
1353 # If one parent of <root> is an ancestor of <dest>, the rebased |
|
1354 # version of this parent will be <dest>. This is always true with |
|
1355 # --base option. |
|
1356 # |
|
1357 # Otherwise, we need to *replace* the original parents with |
|
1358 # <dest>. This "detaches" the rebased set from its former location |
|
1359 # and rebases it onto <dest>. Changes introduced by ancestors of |
|
1360 # <root> not common with <dest> (the detachset, marked as |
|
1361 # nullmerge) are "removed" from the rebased changesets. |
|
1362 # |
|
1363 # - If <root> has a single parent, set it to <dest>. |
|
1364 # |
|
1365 # - If <root> is a merge, we cannot decide which parent to |
|
1366 # replace, the rebase operation is not clearly defined. |
|
1367 # |
|
1368 # The table below sums up this behavior: |
|
1369 # |
|
1370 # +------------------+----------------------+-------------------------+ |
|
1371 # | | one parent | merge | |
|
1372 # +------------------+----------------------+-------------------------+ |
|
1373 # | parent in | new parent is <dest> | parents in ::<dest> are | |
|
1374 # | ::<dest> | | remapped to <dest> | |
|
1375 # +------------------+----------------------+-------------------------+ |
|
1376 # | unrelated source | new parent is <dest> | ambiguous, abort | |
|
1377 # +------------------+----------------------+-------------------------+ |
|
1378 # |
|
1379 # The actual abort is handled by `defineparents` |
|
1380 if len(root.parents()) <= 1: |
|
1381 # ancestors of <root> not ancestors of <dest> |
|
1382 detachset.update(repo.changelog.findmissingrevs([commonbase.rev()], |
|
1383 [root.rev()])) |
|
1384 if emptyrebase: |
1334 if emptyrebase: |
1385 return None |
1335 return None |
1386 for rev in sorted(state): |
1336 for rev in sorted(state): |
1387 parents = [p for p in repo.changelog.parentrevs(rev) if p != nullrev] |
1337 parents = [p for p in repo.changelog.parentrevs(rev) if p != nullrev] |
1388 # if all parents of this revision are done, then so is this revision |
1338 # if all parents of this revision are done, then so is this revision |
1389 if parents and all((state.get(p) == p for p in parents)): |
1339 if parents and all((state.get(p) == p for p in parents)): |
1390 state[rev] = rev |
1340 state[rev] = rev |
1391 for r in detachset: |
|
1392 if r not in state: |
|
1393 state[r] = nullmerge |
|
1394 if len(roots) > 1: |
|
1395 # If we have multiple roots, we may have "hole" in the rebase set. |
|
1396 # Rebase roots that descend from those "hole" should not be detached as |
|
1397 # other root are. We use the special `revignored` to inform rebase that |
|
1398 # the revision should be ignored but that `defineparents` should search |
|
1399 # a rebase destination that make sense regarding rebased topology. |
|
1400 rebasedomain = set(repo.revs('%ld::%ld', rebaseset, rebaseset)) |
|
1401 for ignored in set(rebasedomain) - set(rebaseset): |
|
1402 state[ignored] = revignored |
|
1403 unfi = repo.unfiltered() |
1341 unfi = repo.unfiltered() |
1404 for r in obsoletenotrebased: |
1342 for r in obsoletenotrebased: |
1405 desc = _ctxdesc(unfi[r]) |
1343 desc = _ctxdesc(unfi[r]) |
1406 succ = obsoletenotrebased[r] |
1344 succ = obsoletenotrebased[r] |
1407 if succ is None: |
1345 if succ is None: |