changeset 5778:84affb254cdf

evolvecmd: don't update working copy when using in-memory merge This patch removes the update of the working copy after each in-memory merge and only instead updates the working copy afterwards (if requested). There's still no significant speedup in the hg repo. It seems that the cost of updating the working copy is not large enough to make much difference, and the cost of reading and updating obsmarkers is relatively high in that repo. A significant part of the time (~35%) is spent in `repoview.computehidden()` because it is recalculated for every commit (seems like potential for improvement). I made similar changes in the mozilla-unified repo (which has a significantly larger working copy), by adding 10 commits each changing one line of a file. There, evolving 9 of those commits took 34s before this patch and 20s after. I measured similar speedups in an internal repo (9.0s -> 5.2s).
author Martin von Zweigbergk <martinvonz@google.com>
date Tue, 24 Nov 2020 16:33:24 -0800
parents c5dfbbe4363d
children 96ed73c5c6ca
files hgext3rd/evolve/__init__.py hgext3rd/evolve/evolvecmd.py tests/test-evolve-inmemory.t tests/test-stabilize-order.t
diffstat 4 files changed, 72 insertions(+), 22 deletions(-) [+]
line wrap: on
line diff
--- a/hgext3rd/evolve/__init__.py	Thu Oct 15 15:40:36 2020 -0700
+++ b/hgext3rd/evolve/__init__.py	Tue Nov 24 16:33:24 2020 -0800
@@ -1096,6 +1096,10 @@
                                                              False))
     # making sure a next commit is formed
     if result[0] and result[1]:
+        # If using in-memory merge, _solveone() will not have updated the
+        # working copy, so we need to do that.
+        if evolvecmd.use_in_memory_merge(repo) and result[1]:
+            compat.update(repo[result[1]])
         ui.status(_(b'working directory is now at %s\n')
                   % ui.label(bytes(repo[b'.']), b'evolve.node'))
     return 0
--- a/hgext3rd/evolve/evolvecmd.py	Thu Oct 15 15:40:36 2020 -0700
+++ b/hgext3rd/evolve/evolvecmd.py	Tue Nov 24 16:33:24 2020 -0800
@@ -934,7 +934,7 @@
                            b'not be updated\n') % sha1)
     return commitmsg
 
-def _use_in_memory_merge(repo):
+def use_in_memory_merge(repo):
     config_value = repo.ui.config(b'experimental', b'evolution.in-memory')
     if config_value == b'force':
         return True
@@ -987,7 +987,7 @@
         (b'phases', b'new-commit'): targetphase
     }
     with repo.ui.configoverride(configoverrides, source=b'evolve'):
-        if not update and _use_in_memory_merge(repo):
+        if not update and use_in_memory_merge(repo):
             try:
                 return _relocatecommitinmem(
                     repo, orig, dest, pctx, keepbranch, commitmsg, extra
@@ -1069,11 +1069,7 @@
     )
     if memctx.isempty() and not repo.ui.configbool(b'ui', b'allowemptycommit'):
         return None
-    n = repo.commitctx(memctx)
-    # TODO: Don't update here, update at the end of the whole evolve
-    # operation instead.
-    compat.clean_update(repo[n or dest])
-    return n
+    return repo.commitctx(memctx)
 
 
 def _finalizerelocate(repo, orig, dest, nodenew, tr, category, evolvestate):
@@ -1488,8 +1484,20 @@
 
     return opts
 
-def _cleanup(ui, repo, startnode, shouldupdate):
-    if not shouldupdate:
+def _cleanup(ui, repo, startnode, shouldupdate, headnode):
+    """Update to the right destination after evolving, if necessary
+
+    headnode is the last node created by the evolve operation (which
+    we may need to update to when using in-memory merge)
+    """
+    # If --update was passed, we should update to some head of the evolved set,
+    # but we only need to do that in the in-memory case. If --update was not
+    # passed, we should still update the working copy to its successor if there
+    # is one.
+    if shouldupdate:
+        if use_in_memory_merge(repo) and headnode:
+            compat.update(repo[headnode])
+    else:
         # Move back to startnode, or to its successor if the start node is
         # obsolete (perhaps made obsolete by the current `hg evolve`)
         unfi = repo.unfiltered()
@@ -1684,13 +1692,14 @@
 
     ui.setconfig(b'ui', b'forcemerge', opts.get('tool', r''), b'evolve')
 
+    headnode = None
     evolvestate = state.cmdstate(repo)
     # Continuation handling
     if contopt:
         if not evolvestate:
             raise error.Abort(_(b'no interrupted evolve to continue'))
         evolvestate.load()
-        continueevolve(ui, repo, evolvestate)
+        headnode = continueevolve(ui, repo, evolvestate)
         if evolvestate[b'command'] != b'evolve':
             evolvestate.delete()
             return
@@ -1763,21 +1772,26 @@
         tr = repo.transaction(b"evolve")
         with util.acceptintervention(tr):
             for rev in revs:
-                _solveonerev(ui, repo, rev, evolvestate, activetopic, dryrunopt,
-                             confirmopt, progresscb, targetcat)
+                (solved, newnode) = _solveonerev(ui, repo, rev, evolvestate,
+                                                 activetopic, dryrunopt,
+                                                 confirmopt, progresscb,
+                                                 targetcat)
+                if solved:
+                    headnode = newnode
                 seen += 1
 
         if showprogress:
             compat.progress(ui, _(b'evolve'), None)
 
-    _cleanup(ui, repo, startnode, shouldupdate)
+    _cleanup(ui, repo, startnode, shouldupdate, headnode)
 
 def _solveonerev(ui, repo, rev, evolvestate, activetopic, dryrunopt, confirmopt,
                  progresscb, targetcat):
     """solves one trouble, including orphan merges
 
     Like _solveone(), this solves one trouble. Unlike _solveone(), it
-    stabilizes for both parents of orphan merges.
+    stabilizes for both parents of orphan merges. Returns the same value as
+    _solveone().
     """
     curctx = repo[rev]
     revtopic = getattr(curctx, 'topic', lambda: b'')()
@@ -1810,6 +1824,7 @@
             evolvestate[b'skippedrevs'].append(curctx.node())
 
         evolvestate[b'orphanmerge'] = False
+    return ret
 
 def solveobswdp(ui, repo, opts):
     """this function updates to the successor of obsolete wdir parent"""
@@ -2002,6 +2017,7 @@
         compat.progress(ui, _(b'evolve'), seen, unit=_(b'changesets'),
                         total=count)
 
+    headnode = None
     category = evolvestate[b'category']
     confirm = evolvestate[b'confirm']
     unfi = repo.unfiltered()
@@ -2028,9 +2044,11 @@
                 if newnode[0]:
                     evolvestate[b'replacements'][curctx.node()] = newnode[1]
                     evolvestate[b'lastsolved'] = newnode[1]
+                    headnode = newnode[1]
                 else:
                     evolvestate[b'skippedrevs'].append(curctx.node())
             seen += 1
+    return headnode
 
 def _continuecontentdivergent(ui, repo, evolvestate, progresscb):
     """function to continue the interrupted content-divergence resolution."""
--- a/tests/test-evolve-inmemory.t	Thu Oct 15 15:40:36 2020 -0700
+++ b/tests/test-evolve-inmemory.t	Tue Nov 24 16:33:24 2020 -0800
@@ -144,13 +144,43 @@
   > precommit = echo "running precommit hook"
   > EOF
 The hook is not run with in-memory=force
-  $ hg evolve --config experimental.evolution.in-memory=force
+  $ hg co B2
+  3 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  $ hg evolve --config experimental.evolution.in-memory=force --update
   move:[3] C
   atop:[2] B2
-  $ hg touch tip^
+  working directory is now at 52da76e91abb
+  $ hg glog
+  @  4:52da76e91abb draft tip
+  |  C
+  | x  3:bc77848cde3a draft C
+  | |  C
+  o |  2:377a194b9b8a draft B2
+  | |  B2
+  | x  1:830b6315076c draft B
+  |/   B
+  o  0:426bada5c675 draft A
+     A
+  $ hg co tip^
+  0 files updated, 0 files merged, 2 files removed, 0 files unresolved
+  $ hg amend -m B3
   1 new orphan changesets
 The hook is run with in-memory=yes
-  $ hg evolve --config experimental.evolution.in-memory=yes
+  $ hg next --config experimental.evolution.in-memory=yes
   move:[4] C
-  atop:[5] B2
+  atop:[5] B3
   running precommit hook
+  working directory is now at aeee7323c054
+  $ hg glog
+  @  6:aeee7323c054 draft tip
+  |  C
+  o  5:908ce5f9d7eb draft
+  |  B3
+  | x  3:bc77848cde3a draft C
+  | |  C
+  +---x  2:377a194b9b8a draft B2
+  | |    B2
+  | x  1:830b6315076c draft B
+  |/   B
+  o  0:426bada5c675 draft A
+     A
--- a/tests/test-stabilize-order.t	Thu Oct 15 15:40:36 2020 -0700
+++ b/tests/test-stabilize-order.t	Tue Nov 24 16:33:24 2020 -0800
@@ -80,10 +80,8 @@
   b
   committing manifest
   committing changelog
-  resolving manifests (inmemory !)
-  getting b (inmemory !)
-  resolving manifests
-  removing b
+  resolving manifests (ondisk !)
+  removing b (ondisk !)
   $ glog
   o  6:81b8bbcd5892@default(draft) addb
   |