[PATCH 1 of 4 V4] update: accept --merge to allow merging across topo branches (issue5125)

Martin von Zweigbergk martinvonz at google.com
Mon Feb 27 17:29:08 EST 2017


On Tue, Feb 21, 2017 at 2:27 PM, Martin von Zweigbergk
<martinvonz at google.com> wrote:
> Sure. I'm on vacation, so I can do that next week, or in a follow-up.
> Whichever the person considering queuing prefers.

I'm back from vacation. It's unclear if the series was not queued
because of Jun's comment or something else (maybe lack of time), but
I'm working on a V5 now. I hope to send that quite soon. Let me know
if there was another problem that held the series back.

>
>
> On Tue, Feb 21, 2017, 14:18 Jun Wu <quark at fb.com> wrote:
>>
>> I like the behavior change (didn't check the implementation details
>> carefully).
>>
>> Could you also update the table in the docstring of merge.update? I think
>> that's very helpful to explain the behavior cleanly. Thanks!
>>
>> Excerpts from Martin von Zweigbergk's message of 2017-02-16 08:59:12
>> -0800:
>> > # HG changeset patch
>> > # User Martin von Zweigbergk <martinvonz at google.com>
>> > # Date 1487019517 28800
>> > #      Mon Feb 13 12:58:37 2017 -0800
>> > # Node ID 19f471c814809099b5452b1174a2ecb0699cb76a
>> > # Parent  1ee685defe80117cf6aafea1ede6c33c478abceb
>> > update: accept --merge to allow merging across topo branches (issue5125)
>> >
>> > diff -r 1ee685defe80 -r 19f471c81480 mercurial/commands.py
>> > --- a/mercurial/commands.py    Wed Feb 15 16:29:58 2017 -0800
>> > +++ b/mercurial/commands.py    Mon Feb 13 12:58:37 2017 -0800
>> > @@ -5286,12 +5286,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 local 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
>> > @@ -5310,8 +5311,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
>> > @@ -5323,10 +5324,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
>> > @@ -5351,8 +5356,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)
>> > @@ -5366,7 +5378,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):
>> > diff -r 1ee685defe80 -r 19f471c81480 mercurial/hg.py
>> > --- a/mercurial/hg.py    Wed Feb 15 16:29:58 2017 -0800
>> > +++ b/mercurial/hg.py    Mon Feb 13 12:58:37 2017 -0800
>> > @@ -681,18 +681,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"))
>> > @@ -712,7 +713,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:
>> > @@ -724,10 +725,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
>> > @@ -739,9 +749,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():
>> > diff -r 1ee685defe80 -r 19f471c81480 mercurial/merge.py
>> > --- a/mercurial/merge.py    Wed Feb 15 16:29:58 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
>> >
>> > @@ -1491,9 +1492,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.
>> > @@ -1550,7 +1558,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
>> > diff -r 1ee685defe80 -r 19f471c81480 tests/test-completion.t
>> > --- a/tests/test-completion.t    Wed Feb 15 16:29:58 2017 -0800
>> > +++ b/tests/test-completion.t    Mon Feb 13 12:58:37 2017 -0800
>> > @@ -223,7 +223,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
>> > diff -r 1ee685defe80 -r 19f471c81480 tests/test-update-branches.t
>> > --- a/tests/test-update-branches.t    Wed Feb 15 16:29:58 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
>> >


More information about the Mercurial-devel mailing list