Mercurial > hg
changeset 33619:609606d21765
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
author | Durham Goode <durham@fb.com> |
---|---|
date | Thu, 20 Jul 2017 01:30:41 -0700 |
parents | 658524d45af0 |
children | f3407d56a6e8 |
files | hgext/rebase.py mercurial/dirstateguard.py mercurial/util.py tests/test-rebase-base.t |
diffstat | 4 files changed, 74 insertions(+), 13 deletions(-) [+] |
line wrap: on
line diff
--- 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'
--- 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")
--- 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.
--- 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 <<EOF + > [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 ..