D728: rebase: move working parent movement logic to scmutil.cleanupnodes

quark (Jun Wu) phabricator at mercurial-scm.org
Mon Sep 18 20:21:04 UTC 2017


quark created this revision.
Herald added a subscriber: mercurial-devel.
Herald added a reviewer: hg-reviewers.

REVISION SUMMARY
  Moving working parent is a common requirement that could be shared in other
  places. This patch makes it possible.

REPOSITORY
  rHG Mercurial

REVISION DETAIL
  https://phab.mercurial-scm.org/D728

AFFECTED FILES
  hgext/rebase.py
  mercurial/scmutil.py
  tests/test-rebase-conflicts.t

CHANGE DETAILS

diff --git a/tests/test-rebase-conflicts.t b/tests/test-rebase-conflicts.t
--- a/tests/test-rebase-conflicts.t
+++ b/tests/test-rebase-conflicts.t
@@ -283,7 +283,6 @@
   updating the branch cache
   rebased as 2a7f09cac94c
   rebase merging completed
-  update back to initial working directory parent
   resolving manifests
    branchmerge: False, force: False, partial: False
    ancestor: 2a7f09cac94c, local: 2a7f09cac94c+, remote: d79e2059b5c0
diff --git a/mercurial/scmutil.py b/mercurial/scmutil.py
--- a/mercurial/scmutil.py
+++ b/mercurial/scmutil.py
@@ -579,11 +579,11 @@
     def __contains__(self, node):
         return self._revcontains(self._torev(node))
 
-def cleanupnodes(repo, mapping, operation):
+def cleanupnodes(repo, mapping, operation, workingparent=None):
     """do common cleanups when old nodes are replaced by new nodes
 
-    That includes writing obsmarkers or stripping nodes, and moving bookmarks.
-    (we might also want to move working directory parent in the future)
+    That includes writing obsmarkers or stripping nodes, moving bookmarks, and
+    optionally moving working parent.
 
     mapping is {oldnode: {'new': [newnode], 'move': newnode}}, where 'new'
     decide obsolete markers, and 'move' decide bookmark move, which could be
@@ -596,6 +596,11 @@
 
         - {oldnode: [newnode]}, equal to {oldnode: {'new': [newnode]}}
         - {oldnode}, equal to {oldnode: []}
+
+    If workingparent is not None, it's the desired node that needs to update
+    to. The node may be adjusted using the bookmark movement logic. Uncommitted
+    changes will be discarded, the caller is responsible for checking the
+    cleanness of working copy.
     """
     if not mapping:
         return
@@ -606,13 +611,17 @@
     elif all('new' not in v and 'move' not in v for v in mapping.values()):
         mapping = {n: {'new': newnodes} for n, newnodes in mapping.items()}
 
+    bmarkwp = ''  # an invalid bookmark name to indicate working parent
+
     with repo.transaction('cleanup') as tr:
         # Move bookmarks
         bmarks = repo._bookmarks
         bmarkchanges = []
         allnewnodes = [n for v in mapping.values() for n in v.get('new', ())]
         for oldnode, value in mapping.items():
             oldbmarks = repo.nodebookmarks(oldnode)
+            if workingparent is not None and oldnode == workingparent:
+                oldbmarks = oldbmarks + [bmarkwp]
             if not oldbmarks:
                 continue
             newnode = value.get('move')
@@ -641,13 +650,22 @@
                                    allnewnodes, newnode, oldnode)
             deletenodes = _containsnode(repo, deleterevs)
             for name in oldbmarks:
+                if name is bmarkwp: # working parent adjustment
+                    workingparent = newnode
+                    continue
                 bmarkchanges.append((name, newnode))
                 for b in bookmarks.divergent2delete(repo, deletenodes, name):
                     bmarkchanges.append((b, None))
 
         if bmarkchanges:
             bmarks.applychanges(repo, tr, bmarkchanges)
 
+        # Working parent movement
+        if workingparent and [workingparent] != [r.node()
+                                                 for r in repo[None].parents()]:
+            from . import hg # avoid import cycle
+            hg.updaterepo(repo, workingparent, False)
+
         # Obsolete or strip nodes
         if obsolete.isenabled(repo, obsolete.createmarkersopt):
             # If a node is already obsoleted, and we want to obsolete it
diff --git a/hgext/rebase.py b/hgext/rebase.py
--- a/hgext/rebase.py
+++ b/hgext/rebase.py
@@ -34,7 +34,6 @@
     dirstateguard,
     error,
     extensions,
-    hg,
     lock,
     merge as mergemod,
     mergeutil,
@@ -533,22 +532,12 @@
         if 'qtip' in repo.tags():
             updatemq(repo, self.state, self.skipped, **opts)
 
-        # restore original working directory
-        # (we do this before stripping)
-        newwd = self.state.get(self.originalwd, self.originalwd)
-        if newwd < 0:
-            # original directory is a parent of rebase set root or ignored
-            newwd = self.originalwd
-        if newwd not in [c.rev() for c in repo[None].parents()]:
-            ui.note(_("update back to initial working directory parent\n"))
-            hg.updaterepo(repo, newwd, False)
-
         collapsedas = None
         if not self.keepf:
             if self.collapsef:
                 collapsedas = newnode
         clearrebased(ui, repo, self.destmap, self.state, self.skipped,
-                     collapsedas, self.keepf)
+                     collapsedas, self.keepf, self.originalwd)
 
         clearstatus(repo)
         clearcollapsemsg(repo)
@@ -1513,14 +1502,16 @@
     return originalwd, destmap, state
 
 def clearrebased(ui, repo, destmap, state, skipped, collapsedas=None,
-                 keepf=False):
+                 keepf=False, originalwd=None):
     """dispose of rebased revision at the end of the rebase
 
     If `collapsedas` is not None, the rebase was a collapse whose result if the
     `collapsedas` node.
 
     If `keepf` is not True, the rebase has --keep set and no nodes should be
     removed (but bookmarks still need to be moved).
+
+    If `originalwd` is not None, working parent will be moved.
     """
     tonode = repo.changelog.node
     mapping = {}
@@ -1535,7 +1526,9 @@
                     succs = (newnode,)
                 value['new'] = succs
             mapping[tonode(rev)] = value
-    scmutil.cleanupnodes(repo, mapping, 'rebase')
+    if originalwd is not None:
+        originalwd = repo[originalwd].node()
+    scmutil.cleanupnodes(repo, mapping, 'rebase', workingparent=originalwd)
 
 def pullrebase(orig, ui, repo, *args, **opts):
     'Call rebase after pull if the latter has been invoked with --rebase'



To: quark, #hg-reviewers
Cc: mercurial-devel


More information about the Mercurial-devel mailing list