Patchwork [1,of,4] destupdate: move the check related to the "clean" logic in the function

mail settings
Submitter Pierre-Yves David
Date Oct. 8, 2015, 9:42 p.m.
Message ID <>
Download mbox | patch
Permalink /patch/10893/
State Superseded
Commit 45b86dbabbda0c509da7ce231196108a107b5561
Headers show


Pierre-Yves David - Oct. 8, 2015, 9:42 p.m.
# HG changeset patch
# User Pierre-Yves David <>
# Date 1444042247 25200
#      Mon Oct 05 03:50:47 2015 -0700
# Node ID 6253db3dac7a5614020be223d0debdff9c2feefd
# Parent  a2e4da578aafd000e23cb6e4f42c8ab3d9a5e493
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.


diff --git a/mercurial/ b/mercurial/
--- a/mercurial/
+++ b/mercurial/
@@ -18,11 +18,11 @@  import extensions
 from hgweb import server as hgweb_server
 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
 table = {}
@@ -6572,10 +6572,12 @@  def update(ui, repo, node=None, rev=None
         if check:
             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')
         if clean:
             ret = hg.clean(repo, rev)
diff --git a/mercurial/ b/mercurial/
--- a/mercurial/
+++ b/mercurial/
@@ -10,11 +10,11 @@  from . import (
-def destupdate(repo):
+def destupdate(repo, clean=False):
     """destination for bare update operation
     # Here is where we should consider bookmarks, divergent bookmarks, and tip
     # of current branch; but currently we are only checking the branch tips.
     node = None
@@ -51,6 +51,30 @@  def destupdate(repo):
             successors = [n for sub in successors for n in sub]
             # 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 util.Abort(msg, hint=hint)
+                else:  # destination is not a descendant.
+                    msg = _("not a linear update")
+                    hint = _("merge or update --check to force update")
+                    raise util.Abort(msg, hint=hint)
+    return rev