Patchwork [2,of,2] rebase: use one dirstateguard for when using rebase.singletransaction

login
register
mail settings
Submitter Durham Goode
Date July 11, 2017, 8:54 p.m.
Message ID <d97cae16445737ab0e49.1499806444@dev111.prn1.facebook.com>
Download mbox | patch
Permalink /patch/22238/
State Changes Requested
Headers show

Comments

Durham Goode - July 11, 2017, 8:54 p.m.
# HG changeset patch
# User Durham Goode <durham@fb.com>
# Date 1499806144 25200
#      Tue Jul 11 13:49:04 2017 -0700
# Node ID d97cae16445737ab0e497b00da27fc9643c4ed4e
# Parent  46b1d80be3c6741ba69f5029a8a4315151552293
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.

Patch

diff --git a/hgext/rebase.py b/hgext/rebase.py
--- a/hgext/rebase.py
+++ b/hgext/rebase.py
@@ -479,12 +479,26 @@  class rebaseruntime(object):
                 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')
+            try:
+                newnode = concludenode(repo, revtoreuse, p1, self.external,
+                                       commitmsg=commitmsg,
+                                       extrafn=_makeextrafn(self.extrafns),
+                                       editor=editor,
+                                       keepbranches=self.keepbranchesf,
+                                       date=self.date)
+                if dsguard:
+                    dsguard.close()
+            except error.InterventionRequired:
+                if dsguard:
+                    dsguard.close()
+                raise
+            finally:
+                release(dsguard)
+
             if newnode is None:
                 newrev = self.dest
             else:
@@ -709,18 +723,24 @@  def rebase(ui, repo, **opts):
             if retcode is not None:
                 return retcode
 
-        tr = None
+        tr = dsguard = None
         try:
             if ui.configbool('rebase', 'singletransaction'):
                 tr = repo.transaction('rebase')
+                dsguard = dirstateguard.dirstateguard(repo, 'rebase')
             rbsrt._performrebase(tr)
+            if dsguard:
+                dsguard.close()
             if tr:
                 tr.close()
         except error.InterventionRequired:
+            if dsguard:
+                dsguard.close()
             if tr:
                 tr.close()
             raise
         finally:
+            release(dsguard)
             if tr:
                 tr.release()
 
@@ -849,7 +869,9 @@  def concludenode(repo, rev, p1, p2, comm
     '''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')
+    dsguard = None
+    if not repo.ui.configbool('rebase', 'singletransaction'):
+        dsguard = dirstateguard.dirstateguard(repo, 'rebase')
     try:
         repo.setparents(repo[p1].node(), repo[p2].node())
         ctx = repo[rev]
@@ -872,7 +894,8 @@  def concludenode(repo, rev, p1, p2, comm
                                   date=date, extra=extra, editor=editor)
 
         repo.dirstate.setbranch(repo[newnode].branch())
-        dsguard.close()
+        if dsguard:
+            dsguard.close()
         return newnode
     finally:
         release(dsguard)
diff --git a/tests/test-rebase-base.t b/tests/test-rebase-base.t
--- a/tests/test-rebase-base.t
+++ b/tests/test-rebase-base.t
@@ -379,3 +379,40 @@  Multiple roots. Two children share two p
    /
   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 ..