destupdate: move the check related to the "clean" logic in the function
authorPierre-Yves David <pierre-yves.david@fb.com>
Mon, 05 Oct 2015 03:50:47 -0700
changeset 26628 45b86dbabbda
parent 26627 832c98d79587
child 26629 ae5f7be2b4ab
destupdate: move the check related to the "clean" logic in the function We want this function to exactly predict the behavior for update. Moreover, we would like to remove all high level behavior logic out of the merge module so this is a step forward. Now that the 'destupdate' function both compute and validate the destination, we can directly use it at the command level, ensuring that the 'hg update' command never call 'merge.update' without a defined destination. This is a first (but significant) step toward having 'merge.update' always feed with a properly validated destination and free of high level logic.
mercurial/commands.py
mercurial/destutil.py
--- a/mercurial/commands.py	Mon Oct 12 19:22:34 2015 +0200
+++ b/mercurial/commands.py	Mon Oct 05 03:50:47 2015 -0700
@@ -20,7 +20,7 @@
 import minirst, revset, fileset
 import dagparser, context, simplemerge, graphmod, copies
 import random, operator
-import setdiscovery, treediscovery, dagutil, pvec, localrepo
+import setdiscovery, treediscovery, dagutil, pvec, localrepo, destutil
 import phases, obsolete, exchange, bundle2, repair, lock as lockmod
 import ui as uimod
 
@@ -6597,6 +6597,8 @@
             cmdutil.bailifchanged(repo, merge=False)
             if rev is None:
                 rev = repo[repo[None].branch()].rev()
+        elif rev is None:
+            rev = destutil.destupdate(repo, clean=clean)
 
         repo.ui.setconfig('ui', 'forcemerge', tool, 'update')
 
--- a/mercurial/destutil.py	Mon Oct 12 19:22:34 2015 +0200
+++ b/mercurial/destutil.py	Mon Oct 05 03:50:47 2015 -0700
@@ -11,7 +11,7 @@
     obsolete,
 )
 
-def destupdate(repo):
+def destupdate(repo, clean=False):
     """destination for bare update operation
     """
     # Here is where we should consider bookmarks, divergent bookmarks, and tip
@@ -52,4 +52,28 @@
             # get the max revision for the given successors set,
             # i.e. the 'tip' of a set
             node = repo.revs('max(%ln)', successors).first()
-    return repo[node].rev()
+    rev = repo[node].rev()
+
+    if not clean:
+        # Check that the update is linear.
+        #
+        # Mercurial do not allow update-merge for non linear pattern
+        # (that would be technically possible but was considered too confusing
+        # for user a long time ago)
+        #
+        # See mercurial.merge.update for details
+        if p1.rev() not in repo.changelog.ancestors([rev], inclusive=True):
+            dirty = wc.dirty(missing=True)
+            foreground = obsolete.foreground(repo, [p1.node()])
+            if not repo[rev].node() in foreground:
+                if dirty:
+                    msg = _("uncommitted changes")
+                    hint = _("commit and merge, or update --clean to"
+                             " discard changes")
+                    raise error.Abort(msg, hint=hint)
+                else:  # destination is not a descendant.
+                    msg = _("not a linear update")
+                    hint = _("merge or update --check to force update")
+                    raise error.Abort(msg, hint=hint)
+
+    return rev