# HG changeset patch # User Durham Goode # Date 1500539441 25200 # Node ID 609606d217659e0a6c1cf6f907b6512be5340e57 # Parent 658524d45af0dbb627b25d795ed84227a20fd634 rebase: use one dirstateguard for when using rebase.singletransaction This was previously landed as 2519994d25ca but backed out in b63351f6a2 because it broke hooks mid-rebase and caused conflict resolution data loss in the event of unexpected exceptions. This new version adds the behavior back but behind a config flag, since the performance improvement is notable in large repositories. The old commit message was: 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. (grafted from 9e3dc3a1638b9754b58a0cb26aaa75d868058109) (grafted from 7d38b41d2266d9a02a15c64229fae0da5738dcec) Differential Revision: https://phab.mercurial-scm.org/D135 diff -r 658524d45af0 -r 609606d21765 hgext/rebase.py --- a/hgext/rebase.py Tue Aug 01 10:14:25 2017 -0400 +++ b/hgext/rebase.py Thu Jul 20 01:30:41 2017 -0700 @@ -479,12 +479,17 @@ editopt = True editor = cmdutil.getcommiteditor(edit=editopt, editform=editform) revtoreuse = max(self.state) - newnode = concludenode(repo, revtoreuse, p1, self.external, - commitmsg=commitmsg, - extrafn=_makeextrafn(self.extrafns), - editor=editor, - keepbranches=self.keepbranchesf, - date=self.date) + + dsguard = None + if ui.configbool('rebase', 'singletransaction'): + dsguard = dirstateguard.dirstateguard(repo, 'rebase') + with util.acceptintervention(dsguard): + newnode = concludenode(repo, revtoreuse, p1, self.external, + commitmsg=commitmsg, + extrafn=_makeextrafn(self.extrafns), + editor=editor, + keepbranches=self.keepbranchesf, + date=self.date) if newnode is None: newrev = self.dest else: @@ -711,10 +716,16 @@ return retcode tr = None - if ui.configbool('rebase', 'singletransaction'): + dsguard = None + + singletr = ui.configbool('rebase', 'singletransaction') + if singletr: tr = repo.transaction('rebase') with util.acceptintervention(tr): - rbsrt._performrebase(tr) + if singletr: + dsguard = dirstateguard.dirstateguard(repo, 'rebase') + with util.acceptintervention(dsguard): + rbsrt._performrebase(tr) rbsrt._finishrebase() @@ -841,8 +852,10 @@ '''Commit the wd changes with parents p1 and p2. Reuse commit info from rev but also store useful information in extra. Return node of committed revision.''' - dsguard = dirstateguard.dirstateguard(repo, 'rebase') - try: + dsguard = util.nullcontextmanager() + if not repo.ui.configbool('rebase', 'singletransaction'): + dsguard = dirstateguard.dirstateguard(repo, 'rebase') + with dsguard: repo.setparents(repo[p1].node(), repo[p2].node()) ctx = repo[rev] if commitmsg is None: @@ -864,10 +877,7 @@ date=date, extra=extra, editor=editor) repo.dirstate.setbranch(repo[newnode].branch()) - dsguard.close() return newnode - finally: - release(dsguard) def rebasenode(repo, rev, p1, base, state, collapse, dest): 'Rebase a single revision rev on top of p1 using base as merge ancestor' diff -r 658524d45af0 -r 609606d21765 mercurial/dirstateguard.py --- a/mercurial/dirstateguard.py Tue Aug 01 10:14:25 2017 -0400 +++ b/mercurial/dirstateguard.py Thu Jul 20 01:30:41 2017 -0700 @@ -43,6 +43,16 @@ # ``release(tr, ....)``. self._abort() + def __enter__(self): + return self + + def __exit__(self, exc_type, exc_val, exc_tb): + try: + if exc_type is None: + self.close() + finally: + self.release() + def close(self): if not self._active: # already inactivated msg = (_("can't close already inactivated backup: %s") diff -r 658524d45af0 -r 609606d21765 mercurial/util.py --- a/mercurial/util.py Tue Aug 01 10:14:25 2017 -0400 +++ b/mercurial/util.py Thu Jul 20 01:30:41 2017 -0700 @@ -602,6 +602,10 @@ finally: tr.release() +@contextlib.contextmanager +def nullcontextmanager(): + yield + class _lrucachenode(object): """A node in a doubly linked list. diff -r 658524d45af0 -r 609606d21765 tests/test-rebase-base.t --- a/tests/test-rebase-base.t Tue Aug 01 10:14:25 2017 -0400 +++ b/tests/test-rebase-base.t Thu Jul 20 01:30:41 2017 -0700 @@ -379,3 +379,40 @@ / o 0: A +Rebasing using a single transaction + + $ hg init singletr && cd singletr + $ cat >> .hg/hgrc < [rebase] + > singletransaction=True + > EOF + $ hg debugdrawdag <<'EOF' + > Z + > | + > | D + > | | + > | C + > | | + > Y B + > |/ + > A + > EOF +- We should only see two status stored messages. One from the start, one from +- the end. + $ hg rebase --debug -b D -d Z | grep 'status stored' + rebase status stored + rebase status stored + $ hg tglog + o 5: D + | + o 4: C + | + o 3: B + | + o 2: Z + | + o 1: Y + | + o 0: A + + $ cd ..