--- a/mercurial/commands.py Mon Feb 27 15:09:19 2017 -0800
+++ b/mercurial/commands.py Mon Feb 13 12:58:37 2017 -0800
@@ -5278,12 +5278,13 @@
@command('^update|up|checkout|co',
[('C', 'clean', None, _('discard uncommitted changes (no backup)')),
('c', 'check', None, _('require clean working directory')),
+ ('m', 'merge', None, _('merge uncommitted changes')),
('d', 'date', '', _('tipmost revision matching date'), _('DATE')),
('r', 'rev', '', _('revision'), _('REV'))
] + mergetoolopts,
- _('[-C|-c] [-d DATE] [[-r] REV]'))
+ _('[-C|-c|-m] [-d DATE] [[-r] REV]'))
def update(ui, repo, node=None, rev=None, clean=False, date=None, check=False,
- tool=None):
+ merge=None, tool=None):
"""update working directory (or switch revisions)
Update the repository's working directory to the specified
@@ -5302,8 +5303,8 @@
.. container:: verbose
- The -C/--clean and -c/--check options control what happens if the
- working directory contains uncommitted changes.
+ The -C/--clean, -c/--check, and -m/--merge options control what
+ happens if the working directory contains uncommitted changes.
At most of one of them can be specified.
1. If no option is specified, and if
@@ -5315,10 +5316,14 @@
branch), the update is aborted and the uncommitted changes
are preserved.
- 2. With the -c/--check option, the update is aborted and the
+ 2. With the -m/--merge option, the update is allowed even if the
+ requested changeset is not an ancestor or descendant of
+ the working directory's parent.
+
+ 3. With the -c/--check option, the update is aborted and the
uncommitted changes are preserved.
- 3. With the -C/--clean option, uncommitted changes are discarded and
+ 4. With the -C/--clean option, uncommitted changes are discarded and
the working directory is updated to the requested changeset.
To cancel an uncommitted merge (and lose your changes), use
@@ -5343,8 +5348,15 @@
if date and rev is not None:
raise error.Abort(_("you can't specify a revision and a date"))
- if check and clean:
- raise error.Abort(_("cannot specify both -c/--check and -C/--clean"))
+ if len([x for x in (clean, check, merge) if x]) > 1:
+ raise error.Abort(_("can only specify one of -C/--clean, -c/--check, "
+ "or -m/merge"))
+
+ updatecheck = None
+ if check:
+ updatecheck = 'abort'
+ elif merge:
+ updatecheck = 'none'
with repo.wlock():
cmdutil.clearunfinished(repo)
@@ -5358,7 +5370,8 @@
repo.ui.setconfig('ui', 'forcemerge', tool, 'update')
- return hg.updatetotally(ui, repo, rev, brev, clean=clean, check=check)
+ return hg.updatetotally(ui, repo, rev, brev, clean=clean,
+ updatecheck=updatecheck)
@command('verify', [])
def verify(ui, repo):
--- a/mercurial/hg.py Mon Feb 27 15:09:19 2017 -0800
+++ b/mercurial/hg.py Mon Feb 13 12:58:37 2017 -0800
@@ -691,18 +691,19 @@
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, updatecheck=None):
"""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,
- labels=['working copy', 'destination'])
+ labels=['working copy', 'destination'],
+ updatecheck=updatecheck)
-def update(repo, node, quietempty=False):
- """update the working directory to node, merging linear changes"""
- stats = updaterepo(repo, node, False)
+def update(repo, node, quietempty=False, updatecheck=None):
+ """update the working directory to node"""
+ stats = updaterepo(repo, node, False, updatecheck=updatecheck)
_showstats(repo, stats, quietempty)
if stats[3]:
repo.ui.status(_("use 'hg resolve' to retry unresolved file merges\n"))
@@ -722,7 +723,7 @@
# naming conflict in updatetotally()
_clean = clean
-def updatetotally(ui, repo, checkout, brev, clean=False, check=False):
+def updatetotally(ui, repo, checkout, brev, clean=False, updatecheck=None):
"""Update the working directory with extra care for non-file components
This takes care of non-file components below:
@@ -734,10 +735,19 @@
:checkout: to which revision the working directory is updated
:brev: a name, which might be a bookmark to be activated after updating
:clean: whether changes in the working directory can be discarded
- :check: whether changes in the working directory should be checked
+ :updatecheck: how to deal with a dirty working directory
+
+ Valid values for updatecheck are (None => linear):
+
+ * abort: abort if the working directory is dirty
+ * none: don't check (merge working directory changes into destination)
+ * linear: check that update is linear before merging working directory
+ changes into destination
This returns whether conflict is detected at updating or not.
"""
+ if updatecheck is None:
+ updatecheck = 'linear'
with repo.wlock():
movemarkfrom = None
warndest = False
@@ -749,9 +759,10 @@
if clean:
ret = _clean(repo, checkout)
else:
- if check:
+ if updatecheck == 'abort':
cmdutil.bailifchanged(repo, merge=False)
- ret = _update(repo, checkout)
+ updatecheck = 'none'
+ ret = _update(repo, checkout, updatecheck=updatecheck)
if not ret and movemarkfrom:
if movemarkfrom == repo['.'].node():
--- a/mercurial/merge.py Mon Feb 27 15:09:19 2017 -0800
+++ b/mercurial/merge.py Mon Feb 13 12:58:37 2017 -0800
@@ -1444,7 +1444,8 @@
repo.dirstate.normal(f)
def update(repo, node, branchmerge, force, ancestor=None,
- mergeancestor=False, labels=None, matcher=None, mergeforce=False):
+ mergeancestor=False, labels=None, matcher=None, mergeforce=False,
+ updatecheck=None):
"""
Perform a merge between the working directory and the given node
@@ -1468,14 +1469,17 @@
This logic is tested by test-update-branches.t.
- -c -C dirty rev linear | result
- y y * * * | (1)
- * * * n n | x
- * * n * * | ok
- n n y * y | merge
- n n y y n | (2)
- n y y * * | discard
- y n y * * | (3)
+ -c -C -m dirty rev linear | result
+ y y * * * * | (1)
+ y * y * * * | (1)
+ * y y * * * | (1)
+ * * * * n n | x
+ * * * n * * | ok
+ n n n y * y | merge
+ n n n y y n | (2)
+ n n y y * * | merge
+ n y n y * * | discard
+ y n n y * * | (3)
x = can't happen
* = don't-care
@@ -1486,9 +1490,16 @@
Return the same tuple as applyupdates().
"""
- # This functon used to find the default destination if node was None, but
+ # This function used to find the default destination if node was None, but
# that's now in destutil.py.
assert node is not None
+ if not branchmerge and not force:
+ # TODO: remove the default once all callers that pass branchmerge=False
+ # and force=False pass a value for updatecheck. We may want to allow
+ # updatecheck='abort' to better suppport some of these callers.
+ if updatecheck is None:
+ updatecheck = 'linear'
+ assert updatecheck in ('none', 'linear')
# If we're doing a partial update, we need to skip updating
# the dirstate, so make a note of any partial-ness to the
# update here.
@@ -1545,7 +1556,8 @@
repo.hook('update', parent1=xp2, parent2='', error=0)
return 0, 0, 0, 0
- if pas not in ([p1], [p2]): # nonlinear
+ if (updatecheck == 'linear' and
+ pas not in ([p1], [p2])): # nonlinear
dirty = wc.dirty(missing=True)
if dirty:
# Branching is a bit strange to ensure we do the minimal
--- a/tests/test-completion.t Mon Feb 27 15:09:19 2017 -0800
+++ b/tests/test-completion.t Mon Feb 13 12:58:37 2017 -0800
@@ -228,7 +228,7 @@
serve: accesslog, daemon, daemon-postexec, errorlog, port, address, prefix, name, web-conf, webdir-conf, pid-file, stdio, cmdserver, templates, style, ipv6, certificate
status: all, modified, added, removed, deleted, clean, unknown, ignored, no-status, copies, print0, rev, change, include, exclude, subrepos, template
summary: remote
- update: clean, check, date, rev, tool
+ update: clean, check, merge, date, rev, tool
addremove: similarity, subrepos, include, exclude, dry-run
archive: no-decode, prefix, rev, type, subrepos, include, exclude
backout: merge, commit, no-commit, parent, rev, edit, tool, include, exclude, message, logfile, date, user
--- a/tests/test-update-branches.t Mon Feb 27 15:09:19 2017 -0800
+++ b/tests/test-update-branches.t Mon Feb 13 12:58:37 2017 -0800
@@ -160,6 +160,16 @@
parent=1
M foo
+ $ revtest '-m dirty linear' dirty 1 2 -m
+ 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+ parent=2
+ M foo
+
+ $ revtest '-m dirty cross' dirty 3 4 -m
+ 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+ parent=4
+ M foo
+
$ revtest '-c dirtysub linear' dirtysub 1 2 -c
abort: uncommitted changes in subrepository 'sub'
parent=1
@@ -171,7 +181,17 @@
parent=2
$ revtest '-cC dirty linear' dirty 1 2 -cC
- abort: cannot specify both -c/--check and -C/--clean
+ abort: can only specify one of -C/--clean, -c/--check, or -m/merge
+ parent=1
+ M foo
+
+ $ revtest '-mc dirty linear' dirty 1 2 -mc
+ abort: can only specify one of -C/--clean, -c/--check, or -m/merge
+ parent=1
+ M foo
+
+ $ revtest '-mC dirty linear' dirty 1 2 -mC
+ abort: can only specify one of -C/--clean, -c/--check, or -m/merge
parent=1
M foo