[PATCH 1 of 8 RFC] update: add --guard option to abort if updating modified files

Laurens Holst laurens.nospam at grauw.nl
Wed Dec 21 13:45:51 CST 2011


# HG changeset patch
# User Laurens Holst <laurens.hg at grauw.nl>
# Date 1324423175 -3600
# Node ID 4cff963c895a6465b04a5c6a9e0a207c966e2dcb
# Parent  c153131ce1fc1de11b8725785a51345b8dfa1930
update: add --guard option to abort if updating modified files

If you update while you are working on some changes, when there are conflicts
you are bothered with conflict markers or merge tools and prompts and resolve
commands and all that. Your code that was in a working state will no longer work
until you have resolved the conflicts. Worse, it scatters your focus. In such
case you may rather want to postpone the update until you are done, or commit
your changes first and do a proper merge.

This change adds a --guard/-g flag that aborts the update if it touches any
files that are modified locally and could potentially cause conflicts. The
update could still break your code, however going back to a working version will
now simply be a matter of updating back to the original revision.

A guarded update has the following properties:

1. It does not prompt the user for input
2. It does not alter the user’s changes in the working copy
3. It is reversible, the user can move back his original state just by updating

If any of these can not be satisfied, the update aborts.

To answer the question why you would update while working on a change in the
first place, here are some use cases:

1. To test your local changes on a different version
2. To look something up for a colleague who asks you a question
3. To make the history more linear
4. Out of habit by Subversion refugees

diff -r c153131ce1fc -r 4cff963c895a mercurial/commands.py
--- a/mercurial/commands.py	Tue Dec 20 20:35:21 2011 +0100
+++ b/mercurial/commands.py	Wed Dec 21 00:19:35 2011 +0100
@@ -5590,10 +5590,11 @@
     [('C', 'clean', None, _('discard uncommitted changes (no backup)')),
     ('c', 'check', None,
      _('update across branches if no uncommitted changes')),
+    ('g', 'guard', None, _('abort if there are conflicts or the update is not reversible')),
     ('d', 'date', '', _('tipmost revision matching date'), _('DATE')),
     ('r', 'rev', '', _('revision'), _('REV'))],
     _('[-c] [-C] [-d DATE] [[-r] REV]'))
-def update(ui, repo, node=None, rev=None, clean=False, date=None, check=False):
+def update(ui, repo, node=None, rev=None, clean=False, date=None, check=False, guard=False):
     """update working directory (or switch revisions)
 
     Update the repository's working directory to the specified
@@ -5650,6 +5651,9 @@
     if check and clean:
         raise util.Abort(_("cannot specify both -c/--check and -C/--clean"))
 
+    if guard and clean:
+        raise util.Abort(_("cannot specify both -g/--guard and -C/--clean"))
+
     if check:
         # we could use dirty() but we can ignore merge and branch trivia
         c = repo[None]
@@ -5664,7 +5668,7 @@
     if clean or check:
         ret = hg.clean(repo, rev)
     else:
-        ret = hg.update(repo, rev)
+        ret = hg.update(repo, rev, guard or False)
 
     if brev in repo._bookmarks:
         bookmarks.setcurrent(repo, brev)
diff -r c153131ce1fc -r 4cff963c895a mercurial/hg.py
--- a/mercurial/hg.py	Tue Dec 20 20:35:21 2011 +0100
+++ b/mercurial/hg.py	Wed Dec 21 00:19:35 2011 +0100
@@ -406,9 +406,9 @@
     repo.ui.status(_("%d files updated, %d files merged, "
                      "%d files removed, %d files unresolved\n") % stats)
 
-def update(repo, node):
+def update(repo, node, guard=False):
     """update the working directory to node, merging linear changes"""
-    stats = mergemod.update(repo, node, False, False, None)
+    stats = mergemod.update(repo, node, False, False, None, guard=guard)
     _showstats(repo, stats)
     if stats[3]:
         repo.ui.status(_("use 'hg resolve' to retry unresolved file merges\n"))
diff -r c153131ce1fc -r 4cff963c895a mercurial/merge.py
--- a/mercurial/merge.py	Tue Dec 20 20:35:21 2011 +0100
+++ b/mercurial/merge.py	Wed Dec 21 00:19:35 2011 +0100
@@ -459,7 +459,7 @@
                 if f:
                     repo.dirstate.drop(f)
 
-def update(repo, node, branchmerge, force, partial, ancestor=None):
+def update(repo, node, branchmerge, force, partial, ancestor=None, guard=False):
     """
     Perform a merge between the working directory and the given node
 
@@ -467,6 +467,7 @@
     branchmerge = whether to merge between branches
     force = whether to force branch merging or file overwriting
     partial = a function to filter file lists (dirstate not updated)
+    guard = whether to perform a guarded update
 
     The table below shows all the behaviors of the update command
     given the -c and -C or no options, whether the working directory
@@ -563,6 +564,10 @@
         action += _forgetremoved(wc, p2, branchmerge)
         action += manifestmerge(repo, wc, p2, pa, overwrite, partial)
 
+        ### guarding phase
+        if guard and not isguardsafe(repo, action, wc, p2, pa):
+            raise util.Abort(_("update touches modified files"))
+
         ### apply phase
         if not branchmerge: # just jump to the new rev
             fp1, fp2, xp1, xp2 = fp2, nullid, xp2, ''
@@ -582,3 +587,25 @@
     if not partial:
         repo.hook('update', parent1=xp1, parent2=xp2, error=stats[3])
     return stats
+
+def isguardsafe(repo, action, localctx, otherctx, ancestorctx):
+    """
+    A guarded update has the following properties:
+    
+    1. It does not prompt the user for input
+    2. It does not alter the user's changes in the working copy
+    3. It is reversible, the user can move back his original state by updating
+    
+    If any of these can not be satisfied, the update aborts.
+    
+    This allows the user to update only if there is minimal effort involved,
+    preserve his working code, and postpone any merge-related affairs to when
+    he is ready for it.
+    """
+
+    for a in action:
+        f, m = a[:2]
+        if m == "m":
+            return False
+
+    return True


More information about the Mercurial-devel mailing list