[PATCH RFC] update: add an option to allow to merge local changes when crossing branches

Gilles Moris gilles.moris at free.fr
Fri Feb 22 04:22:07 CST 2013


# HG changeset patch
# User Gilles Moris <gilles.moris at free.fr>
# Date 1361528509 -3600
# Node ID 567106adefd309717d8f0538197dc7dde45d34f1
# Parent  013fcd112f13f31a35ea6a40d8cd1c6923cdaf20
update: add an option to allow to merge local changes when crossing branches

*NOT READY FOR INCLUSION*

Normally, it is not possible to update to another branch with local changes.
One have to use "hg update --clean" to discard changes and force the update.
One workaround is to update to the common ancestor of the branches, and then
to the other branch. This workaround is not well known by users.

I introduce an update --merge option that enables to rebase the local changes
in one step.

However, I have still some questions about how to design it:
I/ option naming
* I used --merge as it seems pretty but though could be confused with
  changesets merge
* --rebase could be used, may be ,ore accurate, but IMO too tied to the rebase
  extension
* --crossbranches would not be a good idea, as we probably don't want to allow
  crossing branches if a revision is not explicitely provided, whether there
  are some local changes or not
* other possibilities: --reseat, --move, ...
* last possibility: no option, allow by default to merge local changes, we've
  got --check if we want to block. By the way, this would greatly simplify both
  the code and the help content. The help content is currently partially lying:
    "If the changeset is not a descendant or ancestor of the working
     directory's parent, the update is aborted."
  Was true before, but since many releases we allow to cross branch for clean
  working directory and an explicit release.
    "-c --check     update across branches if no uncommitted changes"
  Again wrong, this option aborts on any dirty working dir, whether we cross
  branches or not.
  So no option might be more consistent with the evolution of the CLI.

II/ options conflict handling
If we add a new option, we need it to have it mutually exclusive with --clean or
--check options. Do we want --merge be overridden by --check and --clean, so
that a user could habe --merge as an alias default?

III/ internal API changes
I choose the least impacting API change, since I am expecting many extensions to
use those API hg.updaterepo() and merge.update(), even if we are not bound to
keep internal APIs. subrepos and largefiles are clients for instance. Is the
approach OK?

IV/ help and tests will need to be updated once design choices are done.

diff -r 013fcd112f13 -r 567106adefd3 mercurial/commands.py
--- a/mercurial/commands.py	Sat Feb 09 11:00:42 2013 +0100
+++ b/mercurial/commands.py	Fri Feb 22 11:21:49 2013 +0100
@@ -5906,10 +5906,13 @@
     [('C', 'clean', None, _('discard uncommitted changes (no backup)')),
     ('c', 'check', None,
      _('update across branches if no uncommitted changes')),
+    ('m', 'merge', None,
+     _('merge local changes when updating to another branch')),
     ('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,
+           merge=False):
     """update working directory (or switch revisions)
 
     Update the repository's working directory to the specified
@@ -5998,7 +6001,7 @@
     if clean:
         ret = hg.clean(repo, rev)
     else:
-        ret = hg.update(repo, rev)
+        ret = hg.update(repo, rev, merge)
 
     if not ret and movemarkfrom:
         if bookmarks.update(repo, [movemarkfrom], repo['.'].node()):
diff -r 013fcd112f13 -r 567106adefd3 mercurial/hg.py
--- a/mercurial/hg.py	Sat Feb 09 11:00:42 2013 +0100
+++ b/mercurial/hg.py	Fri Feb 22 11:21:49 2013 +0100
@@ -449,17 +449,17 @@
     repo.ui.status(_("%d files updated, %d files merged, "
                      "%d files removed, %d files unresolved\n") % stats)
 
-def updaterepo(repo, node, overwrite):
+def updaterepo(repo, node, overwrite, merge=False):
     """Update the working directory to node.
 
     When overwrite is set, changes are clobbered, merged else
 
     returns stats (see pydoc mercurial.merge.applyupdates)"""
-    return mergemod.update(repo, node, False, overwrite, None)
+    return mergemod.update(repo, node, False, overwrite, None, wdmerge=merge)
 
-def update(repo, node):
+def update(repo, node, merge=False):
     """update the working directory to node, merging linear changes"""
-    stats = updaterepo(repo, node, False)
+    stats = updaterepo(repo, node, False, merge)
     _showstats(repo, stats)
     if stats[3]:
         repo.ui.status(_("use 'hg resolve' to retry unresolved file merges\n"))
diff -r 013fcd112f13 -r 567106adefd3 mercurial/merge.py
--- a/mercurial/merge.py	Sat Feb 09 11:00:42 2013 +0100
+++ b/mercurial/merge.py	Fri Feb 22 11:21:49 2013 +0100
@@ -545,7 +545,7 @@
                     repo.dirstate.drop(f)
 
 def update(repo, node, branchmerge, force, partial, ancestor=None,
-           mergeancestor=False):
+           mergeancestor=False, wdmerge=False):
     """
     Perform a merge between the working directory and the given node
 
@@ -557,6 +557,7 @@
       is only allowed between different named branches. This flag
       is used by rebase extension as a temporary fix and should be
       avoided in general.
+    wdmerge = allow update to rebase local changes across branches
 
     The table below shows all the behaviors of the update command
     given the -c and -C or no options, whether the working directory
@@ -633,11 +634,13 @@
             if pa == p1 or pa == p2: # linear
                 pass # all good
             elif wc.dirty(missing=True):
-                raise util.Abort(_("crosses branches (merge branches or use"
-                                   " --clean to discard changes)"))
+                if not wdmerge:
+                    raise util.Abort(_("crosses branches (merge branches or "
+                                       "use --merge to rebase your changes)"))
+                pa = p1
             elif onode is None:
                 raise util.Abort(_("crosses branches (merge branches or update"
-                                   " --check to force update)"))
+                                   " --merge to force update)"))
             else:
                 # Allow jumping branches if clean and specific rev given
                 pa = p1


More information about the Mercurial-devel mailing list