diff --git a/mercurial/commands.py b/mercurial/commands.py
--- a/mercurial/commands.py
+++ b/mercurial/commands.py
@@ -20,7 +20,7 @@ import merge as mergemod
 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 @@ def update(ui, repo, node=None, rev=None
             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')
 
diff --git a/mercurial/destutil.py b/mercurial/destutil.py
--- a/mercurial/destutil.py
+++ b/mercurial/destutil.py
@@ -11,7 +11,7 @@ from . import (
     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 @@ def destupdate(repo):
             # 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