Mercurial > hg
comparison hgext/rebase.py @ 34008:9422107a6b64
rebase: move working parent and bookmark for obsoleted revs (BC)
Previously, obsoleted revs with successors in destination are completely
ignored. That caused some inconvenience when working copy is obsoleted. Most
commands avoid working copy being obsoleted, but `hg pull` is an exception.
This patch makes rebase able to move bookmarks or working parent for those
obsoleted revs. It does so by keeping the obsoleted revs in `state` and
marking them as "skipped, rebased to desired destination" during run-time.
This reverts part of the behavior change of 3b7cb3d17137 and D24.
Differential Revision: https://phab.mercurial-scm.org/D527
author | Jun Wu <quark@fb.com> |
---|---|
date | Sun, 27 Aug 2017 02:47:47 -0700 |
parents | 06b0cc2588de |
children | 79ab5369d55a |
comparison
equal
deleted
inserted
replaced
34007:06b0cc2588de | 34008:9422107a6b64 |
---|---|
325 raise error.Abort( | 325 raise error.Abort( |
326 _("can't remove original changesets with" | 326 _("can't remove original changesets with" |
327 " unrebased descendants"), | 327 " unrebased descendants"), |
328 hint=_('use --keep to keep original changesets')) | 328 hint=_('use --keep to keep original changesets')) |
329 | 329 |
330 obsrevs = _filterobsoleterevs(self.repo, rebaseset) | 330 result = buildstate(self.repo, destmap, self.collapsef) |
331 self._handleskippingobsolete(obsrevs, destmap) | |
332 | |
333 result = buildstate(self.repo, destmap, self.collapsef, | |
334 self.obsoletenotrebased) | |
335 | 331 |
336 if not result: | 332 if not result: |
337 # Empty state built, nothing to rebase | 333 # Empty state built, nothing to rebase |
338 self.ui.status(_('nothing to rebase\n')) | 334 self.ui.status(_('nothing to rebase\n')) |
339 return _nothingtorebase() | 335 return _nothingtorebase() |
373 branches.add(repo[rev].branch()) | 369 branches.add(repo[rev].branch()) |
374 if len(branches) > 1: | 370 if len(branches) > 1: |
375 raise error.Abort(_('cannot collapse multiple named ' | 371 raise error.Abort(_('cannot collapse multiple named ' |
376 'branches')) | 372 'branches')) |
377 | 373 |
374 # Calculate self.obsoletenotrebased | |
375 obsrevs = _filterobsoleterevs(self.repo, self.state) | |
376 self._handleskippingobsolete(obsrevs, self.destmap) | |
377 | |
378 # Keep track of the active bookmarks in order to reset them later | 378 # Keep track of the active bookmarks in order to reset them later |
379 self.activebookmark = self.activebookmark or repo._activebookmark | 379 self.activebookmark = self.activebookmark or repo._activebookmark |
380 if self.activebookmark: | 380 if self.activebookmark: |
381 bookmarks.deactivate(repo) | 381 bookmarks.deactivate(repo) |
382 | 382 |
399 dest = self.destmap[rev] | 399 dest = self.destmap[rev] |
400 ctx = repo[rev] | 400 ctx = repo[rev] |
401 desc = _ctxdesc(ctx) | 401 desc = _ctxdesc(ctx) |
402 if self.state[rev] == rev: | 402 if self.state[rev] == rev: |
403 ui.status(_('already rebased %s\n') % desc) | 403 ui.status(_('already rebased %s\n') % desc) |
404 elif rev in self.obsoletenotrebased: | |
405 succ = self.obsoletenotrebased[rev] | |
406 if succ is None: | |
407 msg = _('note: not rebasing %s, it has no ' | |
408 'successor\n') % desc | |
409 else: | |
410 succctx = repo[succ] | |
411 succdesc = '%d:%s "%s"' % ( | |
412 succctx.rev(), succctx, | |
413 succctx.description().split('\n', 1)[0]) | |
414 msg = (_('note: not rebasing %s, already in ' | |
415 'destination as %s\n') % (desc, succdesc)) | |
416 repo.ui.status(msg) | |
417 # Make clearrebased aware state[rev] is not a true successor | |
418 self.skipped.add(rev) | |
419 # Record rev as moved to its desired destination in self.state. | |
420 # This helps bookmark and working parent movement. | |
421 dest = max(adjustdest(repo, rev, self.destmap, self.state, | |
422 self.skipped)) | |
423 self.state[rev] = dest | |
404 elif self.state[rev] == revtodo: | 424 elif self.state[rev] == revtodo: |
405 pos += 1 | 425 pos += 1 |
406 ui.status(_('rebasing %s\n') % desc) | 426 ui.status(_('rebasing %s\n') % desc) |
407 ui.progress(_("rebasing"), pos, ("%d:%s" % (rev, ctx)), | 427 ui.progress(_("rebasing"), pos, ("%d:%s" % (rev, ctx)), |
408 _('changesets'), total) | 428 _('changesets'), total) |
409 p1, p2, base = defineparents(repo, rev, self.destmap, | 429 p1, p2, base = defineparents(repo, rev, self.destmap, |
410 self.state) | 430 self.state, self.skipped, |
431 self.obsoletenotrebased) | |
411 self.storestatus(tr=tr) | 432 self.storestatus(tr=tr) |
412 storecollapsemsg(repo, self.collapsemsg) | 433 storecollapsemsg(repo, self.collapsemsg) |
413 if len(repo[None].parents()) == 2: | 434 if len(repo[None].parents()) == 2: |
414 repo.ui.debug('resuming interrupted rebase\n') | 435 repo.ui.debug('resuming interrupted rebase\n') |
415 else: | 436 else: |
460 | 481 |
461 def _finishrebase(self): | 482 def _finishrebase(self): |
462 repo, ui, opts = self.repo, self.ui, self.opts | 483 repo, ui, opts = self.repo, self.ui, self.opts |
463 if self.collapsef and not self.keepopen: | 484 if self.collapsef and not self.keepopen: |
464 p1, p2, _base = defineparents(repo, min(self.state), self.destmap, | 485 p1, p2, _base = defineparents(repo, min(self.state), self.destmap, |
465 self.state) | 486 self.state, self.skipped, |
487 self.obsoletenotrebased) | |
466 editopt = opts.get('edit') | 488 editopt = opts.get('edit') |
467 editform = 'rebase.collapse' | 489 editform = 'rebase.collapse' |
468 if self.collapsemsg: | 490 if self.collapsemsg: |
469 commitmsg = self.collapsemsg | 491 commitmsg = self.collapsemsg |
470 else: | 492 else: |
933 # performed in the destination. | 955 # performed in the destination. |
934 p1rev = repo[rev].p1().rev() | 956 p1rev = repo[rev].p1().rev() |
935 copies.duplicatecopies(repo, rev, p1rev, skiprev=dest) | 957 copies.duplicatecopies(repo, rev, p1rev, skiprev=dest) |
936 return stats | 958 return stats |
937 | 959 |
938 def adjustdest(repo, rev, destmap, state): | 960 def adjustdest(repo, rev, destmap, state, skipped): |
939 """adjust rebase destination given the current rebase state | 961 """adjust rebase destination given the current rebase state |
940 | 962 |
941 rev is what is being rebased. Return a list of two revs, which are the | 963 rev is what is being rebased. Return a list of two revs, which are the |
942 adjusted destinations for rev's p1 and p2, respectively. If a parent is | 964 adjusted destinations for rev's p1 and p2, respectively. If a parent is |
943 nullrev, return dest without adjustment for it. | 965 nullrev, return dest without adjustment for it. |
987 \ / | 1009 \ / |
988 A | 1010 A |
989 """ | 1011 """ |
990 # pick already rebased revs with same dest from state as interesting source | 1012 # pick already rebased revs with same dest from state as interesting source |
991 dest = destmap[rev] | 1013 dest = destmap[rev] |
992 source = [s for s, d in state.items() if d > 0 and destmap[s] == dest] | 1014 source = [s for s, d in state.items() |
1015 if d > 0 and destmap[s] == dest and s not in skipped] | |
993 | 1016 |
994 result = [] | 1017 result = [] |
995 for prev in repo.changelog.parentrevs(rev): | 1018 for prev in repo.changelog.parentrevs(rev): |
996 adjusted = dest | 1019 adjusted = dest |
997 if prev != nullrev: | 1020 if prev != nullrev: |
1035 nodemap = unfi.changelog.nodemap | 1058 nodemap = unfi.changelog.nodemap |
1036 for s in obsutil.allsuccessors(unfi.obsstore, [unfi[rev].node()]): | 1059 for s in obsutil.allsuccessors(unfi.obsstore, [unfi[rev].node()]): |
1037 if s in nodemap: | 1060 if s in nodemap: |
1038 yield nodemap[s] | 1061 yield nodemap[s] |
1039 | 1062 |
1040 def defineparents(repo, rev, destmap, state): | 1063 def defineparents(repo, rev, destmap, state, skipped, obsskipped): |
1041 """Return new parents and optionally a merge base for rev being rebased | 1064 """Return new parents and optionally a merge base for rev being rebased |
1042 | 1065 |
1043 The destination specified by "dest" cannot always be used directly because | 1066 The destination specified by "dest" cannot always be used directly because |
1044 previously rebase result could affect destination. For example, | 1067 previously rebase result could affect destination. For example, |
1045 | 1068 |
1062 return cl.isancestor(cl.node(a), cl.node(b)) | 1085 return cl.isancestor(cl.node(a), cl.node(b)) |
1063 | 1086 |
1064 dest = destmap[rev] | 1087 dest = destmap[rev] |
1065 oldps = repo.changelog.parentrevs(rev) # old parents | 1088 oldps = repo.changelog.parentrevs(rev) # old parents |
1066 newps = [nullrev, nullrev] # new parents | 1089 newps = [nullrev, nullrev] # new parents |
1067 dests = adjustdest(repo, rev, destmap, state) # adjusted destinations | 1090 dests = adjustdest(repo, rev, destmap, state, skipped) |
1068 bases = list(oldps) # merge base candidates, initially just old parents | 1091 bases = list(oldps) # merge base candidates, initially just old parents |
1069 | 1092 |
1070 if all(r == nullrev for r in oldps[1:]): | 1093 if all(r == nullrev for r in oldps[1:]): |
1071 # For non-merge changeset, just move p to adjusted dest as requested. | 1094 # For non-merge changeset, just move p to adjusted dest as requested. |
1072 newps[0] = dests[0] | 1095 newps[0] = dests[0] |
1189 bases, base, base, dest)) | 1212 bases, base, base, dest)) |
1190 | 1213 |
1191 # If those revisions are covered by rebaseset, the result is good. | 1214 # If those revisions are covered by rebaseset, the result is good. |
1192 # A merge in rebaseset would be considered to cover its ancestors. | 1215 # A merge in rebaseset would be considered to cover its ancestors. |
1193 if siderevs: | 1216 if siderevs: |
1194 rebaseset = [r for r, d in state.items() if d > 0] | 1217 rebaseset = [r for r, d in state.items() |
1218 if d > 0 and r not in obsskipped] | |
1195 merges = [r for r in rebaseset | 1219 merges = [r for r in rebaseset |
1196 if cl.parentrevs(r)[1] != nullrev] | 1220 if cl.parentrevs(r)[1] != nullrev] |
1197 unwanted[i] = list(repo.revs('%ld - (::%ld) - %ld', | 1221 unwanted[i] = list(repo.revs('%ld - (::%ld) - %ld', |
1198 siderevs, merges, rebaseset)) | 1222 siderevs, merges, rebaseset)) |
1199 | 1223 |
1406 if not result: | 1430 if not result: |
1407 raise error.Abort(_('source and destination form a cycle')) | 1431 raise error.Abort(_('source and destination form a cycle')) |
1408 srcset -= set(result) | 1432 srcset -= set(result) |
1409 yield result | 1433 yield result |
1410 | 1434 |
1411 def buildstate(repo, destmap, collapse, obsoletenotrebased): | 1435 def buildstate(repo, destmap, collapse): |
1412 '''Define which revisions are going to be rebased and where | 1436 '''Define which revisions are going to be rebased and where |
1413 | 1437 |
1414 repo: repo | 1438 repo: repo |
1415 destmap: {srcrev: destrev} | 1439 destmap: {srcrev: destrev} |
1416 ''' | 1440 ''' |
1467 for rev in sorted(state): | 1491 for rev in sorted(state): |
1468 parents = [p for p in repo.changelog.parentrevs(rev) if p != nullrev] | 1492 parents = [p for p in repo.changelog.parentrevs(rev) if p != nullrev] |
1469 # if all parents of this revision are done, then so is this revision | 1493 # if all parents of this revision are done, then so is this revision |
1470 if parents and all((state.get(p) == p for p in parents)): | 1494 if parents and all((state.get(p) == p for p in parents)): |
1471 state[rev] = rev | 1495 state[rev] = rev |
1472 unfi = repo.unfiltered() | |
1473 for r in obsoletenotrebased: | |
1474 desc = _ctxdesc(unfi[r]) | |
1475 succ = obsoletenotrebased[r] | |
1476 if succ is None: | |
1477 msg = _('note: not rebasing %s, it has no successor\n') % desc | |
1478 del state[r] | |
1479 del destmap[r] | |
1480 else: | |
1481 destctx = unfi[succ] | |
1482 destdesc = '%d:%s "%s"' % (destctx.rev(), destctx, | |
1483 destctx.description().split('\n', 1)[0]) | |
1484 msg = (_('note: not rebasing %s, already in destination as %s\n') | |
1485 % (desc, destdesc)) | |
1486 del state[r] | |
1487 del destmap[r] | |
1488 repo.ui.status(msg) | |
1489 return originalwd, destmap, state | 1496 return originalwd, destmap, state |
1490 | 1497 |
1491 def clearrebased(ui, repo, destmap, state, skipped, collapsedas=None): | 1498 def clearrebased(ui, repo, destmap, state, skipped, collapsedas=None): |
1492 """dispose of rebased revision at the end of the rebase | 1499 """dispose of rebased revision at the end of the rebase |
1493 | 1500 |