[PATCH] Add support for 'union' merge strategy

Matt Harbison matt_harbison at yahoo.com
Fri Jun 26 17:41:30 CDT 2015


On Fri, 26 Jun 2015 15:24:53 -0400, Erik Huelsmann <ehuels at gmail.com>  
wrote:

> As discussed on IRC, I'm submitting my first mercurial patch: to support
> the 'union' merge strategy, where both sides of a conflict are included  
> in
> the result and not marking the file as conflicted.
>
> Please tell me how to proceed. Thanks for your comments and help!
>
> Regards,
>
> Erik.

Hi Erik,

Take a look at the commit guidelines [1].  There are a few things about  
the commit message that can be cleaned up.  It would probably be useful to  
explain in the commit message what a 'union' merge is.  The blurb outside  
the patch doesn't get brought along into the repo, making it harder to  
figure out what it is supposed to do.

You might want to enable --git diffs in your configuration, and use the  
patchbomb extension (or email in TortoiseHg).

I'm not well versed in merge code myself, so I can't offer comments on the  
code.  But you will want to add a test [2], so it doesn't silently break  
in the future.

When you get that fixed up and resend, the convention is to use a V2  
--flag.

--Matt

[1] https://mercurial.selenic.com/wiki/ContributingChanges
[2] https://mercurial.selenic.com/wiki/WritingTests

> # HG changeset patch
> # User erik <erik at hucs.nl>
> # Date 1435346089 -7200
> #      Fri Jun 26 21:14:49 2015 +0200
> # Node ID d35c0f4e71ed16627a2c95539121bfda2b253157
> # Parent  ff5172c830022b64cc5bd1bae36b2276e9dc6e5d
> internal merge tool: add support for 'union' merge strategy.
>
> One use-case for this merge strategy is the Changelog file being changed
> on multiple branches and conflicting when being merged back to the main
> branch.
>
> diff -r ff5172c83002 -r d35c0f4e71ed mercurial/filemerge.py
> --- a/mercurial/filemerge.py Wed Jun 24 13:41:27 2015 -0500
> +++ b/mercurial/filemerge.py Fri Jun 26 21:14:49 2015 +0200
> @@ -212,10 +212,7 @@
>              util.copyfile(back, a) # restore from backup and try again
>      return 1 # continue merging
>
> - at internaltool('merge', True,
> -              _("merging %s incomplete! "
> -                "(edit conflicts, then use 'hg resolve --mark')\n"))
> -def _imerge(repo, mynode, orig, fcd, fco, fca, toolconf, files,
> labels=None):
> +def __imerge(repo, mynode, orig, fcd, fco, fca, toolconf, files, labels,
> mode):
>      """
>      Uses the internal non-interactive simple merge algorithm for merging
>      files. It will fail if there are any conflicts and leave markers in
> @@ -232,10 +229,38 @@
>
>          ui = repo.ui
>
> -        r = simplemerge.simplemerge(ui, a, b, c, label=labels)
> +        r = simplemerge.simplemerge(ui, a, b, c, label=labels,  
> mode=mode)
>          return True, r
>      return False, 0
>
> +
> + at internaltool('merge', True,
> +              _("merging %s incomplete! "
> +                "(edit conflicts, then use 'hg resolve --mark')\n"))
> +def _imerge(repo, mynode, orig, fcd, fco, fca, toolconf, files,
> labels=None):
> +    """
> +    Uses the internal non-interactive simple merge algorithm for merging
> +    files. It will fail if there are any conflicts and leave markers in
> +    the partially merged file. Markers will have two sections, one for
> each side
> +    of merge."""
> +
> +    return __imerge(repo, mynode, orig, fcd, fco, fca, toolconf,
> +                    files, labels, 'merge')
> +
> +
> + at internaltool('union', True,
> +              _("merging %s incomplete! "
> +                "(edit conflicts, then use 'hg resolve --mark')\n"))
> +def _imerge(repo, mynode, orig, fcd, fco, fca, toolconf, files,
> labels=None):
> +    """
> +    Uses the internal non-interactive union merge algorithm for merging
> +    files. It will use both sides if there are any conflicts."""
> +
> +    return __imerge(repo, mynode, orig, fcd, fco, fca, toolconf,
> +                    files, labels, 'union')
> +
> +
> +
>  @internaltool('merge3', True,
>                _("merging %s incomplete! "
>                  "(edit conflicts, then use 'hg resolve --mark')\n"))
> diff -r ff5172c83002 -r d35c0f4e71ed mercurial/simplemerge.py
> --- a/mercurial/simplemerge.py Wed Jun 24 13:41:27 2015 -0500
> +++ b/mercurial/simplemerge.py Fri Jun 26 21:14:49 2015 +0200
> @@ -92,9 +92,9 @@
>                  newline = '\r\n'
>              elif self.a[0].endswith('\r'):
>                  newline = '\r'
> -        if name_a:
> +        if name_a and start_marker:
>              start_marker = start_marker + ' ' + name_a
> -        if name_b:
> +        if name_b and end_marker:
>              end_marker = end_marker + ' ' + name_b
>          if name_base and base_marker:
>              base_marker = base_marker + ' ' + name_base
> @@ -112,17 +112,20 @@
>                      yield self.b[i]
>              elif what == 'conflict':
>                  self.conflicts = True
> -                yield start_marker + newline
> +                if start_marker is not None:
> +                    yield start_marker + newline
>                  for i in range(t[3], t[4]):
>                      yield self.a[i]
>                  if base_marker is not None:
>                      yield base_marker + newline
>                      for i in range(t[1], t[2]):
>                          yield self.base[i]
> -                yield mid_marker + newline
> +                if mid_marker is not None:
> +                    yield mid_marker + newline
>                  for i in range(t[5], t[6]):
>                      yield self.b[i]
> -                yield end_marker + newline
> +                if end_marker is not None:
> +                    yield end_marker + newline
>              else:
>                  raise ValueError(what)
>
> @@ -345,18 +348,24 @@
>                  raise util.Abort(msg)
>          return text
>
> -    name_a = local
> -    name_b = other
> -    name_base = None
> -    labels = opts.get('label', [])
> -    if len(labels) > 0:
> -        name_a = labels[0]
> -    if len(labels) > 1:
> -        name_b = labels[1]
> -    if len(labels) > 2:
> -        name_base = labels[2]
> -    if len(labels) > 3:
> -        raise util.Abort(_("can only specify three labels."))
> +    mode = opts.get('mode', '')
> +    if mode == 'union':
> +        name_a = None
> +        name_b = None
> +        name_base = None
> +    else:
> +        name_a = local
> +        name_b = other
> +        name_base = None
> +        labels = opts.get('label', [])
> +        if len(labels) > 0:
> +            name_a = labels[0]
> +        if len(labels) > 1:
> +            name_b = labels[1]
> +        if len(labels) > 2:
> +            name_base = labels[2]
> +        if len(labels) > 3:
> +            raise util.Abort(_("can only specify three labels."))
>
>      try:
>          localtext = readfile(local)
> @@ -374,7 +383,11 @@
>
>      m3 = Merge3Text(basetext, localtext, othertext)
>      extrakwargs = {}
> -    if name_base is not None:
> +    if mode == 'union':
> +        extrakwargs['start_marker'] = None
> +        extrakwargs['end_marker'] = None
> +        extrakwargs['mid_marker'] = None
> +    elif name_base is not None:
>          extrakwargs['base_marker'] = '|||||||'
>          extrakwargs['name_base'] = name_base
>      for line in m3.merge_lines(name_a=name_a, name_b=name_b,
> **extrakwargs):
> @@ -383,7 +396,7 @@
>      if not opts.get('print'):
>          out.close()
>
> -    if m3.conflicts:
> +    if m3.conflicts and not mode == 'union':
>          if not opts.get('quiet'):
>              ui.warn(_("warning: conflicts during merge.\n"))
>          return 1
>



More information about the Mercurial-devel mailing list