comparison hgext/rebase.py @ 31514:2519994d25ca

rebase: use one dirstateguard for entire rebase Recently we switched rebases to run the entire rebase inside a single transaction, which dramatically improved the speed of rebases in repos with large working copies. Let's also move the dirstate into a single dirstateguard to get the same benefits. This let's us avoid serializing the dirstate after each commit. In a large repo, rebasing 27 commits is sped up by about 20%. I believe the test changes are because us touching the dirstate gave the transaction something to actually rollback.
author Durham Goode <durham@fb.com>
date Sun, 19 Mar 2017 11:54:15 -0700
parents f255b1811f5e
children 13dc00c233b7
comparison
equal deleted inserted replaced
31513:68474b72ea63 31514:2519994d25ca
473 self.state[rebased] > nullmerge: 473 self.state[rebased] > nullmerge:
474 commitmsg += '\n* %s' % repo[rebased].description() 474 commitmsg += '\n* %s' % repo[rebased].description()
475 editopt = True 475 editopt = True
476 editor = cmdutil.getcommiteditor(edit=editopt, editform=editform) 476 editor = cmdutil.getcommiteditor(edit=editopt, editform=editform)
477 revtoreuse = max(self.state) 477 revtoreuse = max(self.state)
478 newnode = concludenode(repo, revtoreuse, p1, self.external, 478 dsguard = dirstateguard.dirstateguard(repo, 'rebase')
479 commitmsg=commitmsg, 479 try:
480 extrafn=_makeextrafn(self.extrafns), 480 newnode = concludenode(repo, revtoreuse, p1, self.external,
481 editor=editor, 481 commitmsg=commitmsg,
482 keepbranches=self.keepbranchesf, 482 extrafn=_makeextrafn(self.extrafns),
483 date=self.date) 483 editor=editor,
484 keepbranches=self.keepbranchesf,
485 date=self.date)
486 dsguard.close()
487 release(dsguard)
488 except error.InterventionRequired:
489 dsguard.close()
490 release(dsguard)
491 raise
492 except Exception:
493 release(dsguard)
494 raise
495
484 if newnode is None: 496 if newnode is None:
485 newrev = self.target 497 newrev = self.target
486 else: 498 else:
487 newrev = repo[newnode].rev() 499 newrev = repo[newnode].rev()
488 for oldrev in self.state.iterkeys(): 500 for oldrev in self.state.iterkeys():
710 retcode = rbsrt._preparenewrebase(dest, rebaseset) 722 retcode = rbsrt._preparenewrebase(dest, rebaseset)
711 if retcode is not None: 723 if retcode is not None:
712 return retcode 724 return retcode
713 725
714 with repo.transaction('rebase') as tr: 726 with repo.transaction('rebase') as tr:
727 dsguard = dirstateguard.dirstateguard(repo, 'rebase')
715 try: 728 try:
716 rbsrt._performrebase(tr) 729 rbsrt._performrebase(tr)
730 dsguard.close()
731 release(dsguard)
717 except error.InterventionRequired: 732 except error.InterventionRequired:
733 dsguard.close()
734 release(dsguard)
718 tr.close() 735 tr.close()
736 raise
737 except Exception:
738 release(dsguard)
719 raise 739 raise
720 rbsrt._finishrebase() 740 rbsrt._finishrebase()
721 finally: 741 finally:
722 release(lock, wlock) 742 release(lock, wlock)
723 743
838 def concludenode(repo, rev, p1, p2, commitmsg=None, editor=None, extrafn=None, 858 def concludenode(repo, rev, p1, p2, commitmsg=None, editor=None, extrafn=None,
839 keepbranches=False, date=None): 859 keepbranches=False, date=None):
840 '''Commit the wd changes with parents p1 and p2. Reuse commit info from rev 860 '''Commit the wd changes with parents p1 and p2. Reuse commit info from rev
841 but also store useful information in extra. 861 but also store useful information in extra.
842 Return node of committed revision.''' 862 Return node of committed revision.'''
843 dsguard = dirstateguard.dirstateguard(repo, 'rebase') 863 repo.setparents(repo[p1].node(), repo[p2].node())
844 try: 864 ctx = repo[rev]
845 repo.setparents(repo[p1].node(), repo[p2].node()) 865 if commitmsg is None:
846 ctx = repo[rev] 866 commitmsg = ctx.description()
847 if commitmsg is None: 867 keepbranch = keepbranches and repo[p1].branch() != ctx.branch()
848 commitmsg = ctx.description() 868 extra = {'rebase_source': ctx.hex()}
849 keepbranch = keepbranches and repo[p1].branch() != ctx.branch() 869 if extrafn:
850 extra = {'rebase_source': ctx.hex()} 870 extrafn(ctx, extra)
851 if extrafn: 871
852 extrafn(ctx, extra) 872 targetphase = max(ctx.phase(), phases.draft)
853 873 overrides = {('phases', 'new-commit'): targetphase}
854 targetphase = max(ctx.phase(), phases.draft) 874 with repo.ui.configoverride(overrides, 'rebase'):
855 overrides = {('phases', 'new-commit'): targetphase} 875 if keepbranch:
856 with repo.ui.configoverride(overrides, 'rebase'): 876 repo.ui.setconfig('ui', 'allowemptycommit', True)
857 if keepbranch: 877 # Commit might fail if unresolved files exist
858 repo.ui.setconfig('ui', 'allowemptycommit', True) 878 if date is None:
859 # Commit might fail if unresolved files exist 879 date = ctx.date()
860 if date is None: 880 newnode = repo.commit(text=commitmsg, user=ctx.user(),
861 date = ctx.date() 881 date=date, extra=extra, editor=editor)
862 newnode = repo.commit(text=commitmsg, user=ctx.user(), 882
863 date=date, extra=extra, editor=editor) 883 repo.dirstate.setbranch(repo[newnode].branch())
864 884 return newnode
865 repo.dirstate.setbranch(repo[newnode].branch())
866 dsguard.close()
867 return newnode
868 finally:
869 release(dsguard)
870 885
871 def rebasenode(repo, rev, p1, base, state, collapse, target): 886 def rebasenode(repo, rev, p1, base, state, collapse, target):
872 'Rebase a single revision rev on top of p1 using base as merge ancestor' 887 'Rebase a single revision rev on top of p1 using base as merge ancestor'
873 # Merge phase 888 # Merge phase
874 # Update to target and merge it with local 889 # Update to target and merge it with local