Patchwork filemerge: add non-interactive internal:merge-local and internal:merge-other

login
register
mail settings
Submitter Jordi Gutiérrez Hermoso
Date Aug. 11, 2014, 7:37 p.m.
Message ID <5ae2452544337f9d40e8.1407785850@Iris>
Download mbox | patch
Permalink /patch/5351/
State Superseded
Headers show

Comments

Jordi Gutiérrez Hermoso - Aug. 11, 2014, 7:37 p.m.
# HG changeset patch
# User Jordi Gutiérrez Hermoso <jordigh@octave.org>
# Date 1407783206 14400
#      Mon Aug 11 14:53:26 2014 -0400
# Node ID 5ae2452544337f9d40e8ee540d8038fd0a03cb44
# Parent  445472225ccd07c27323cab59c4a1bf728f76142
filemerge: add non-interactive internal:merge-local and internal:merge-other

There are two non-interactive internal merge tools, internal:other and
internal:local, but they don't really merge, they just pick all
changes from the local or other version of the file. In some
situations, it is known that we want a merge and also know that all
merge conflicts should be resolved in one direction. Although external
merge tools can do this, sometimes it can be convenient to do so from
within hg, without invoking a merge tool. These new
internal:merge-local and internal:merge-other tools can do just that.
Jordi Gutiérrez Hermoso - Aug. 12, 2014, 1:06 p.m.
On Mon, 2014-08-11 at 15:37 -0400, Jordi Gutiérrez Hermoso wrote:
> # HG changeset patch
> # User Jordi Gutiérrez Hermoso <jordigh@octave.org>
> # Date 1407783206 14400
> #      Mon Aug 11 14:53:26 2014 -0400
> # Node ID 5ae2452544337f9d40e8ee540d8038fd0a03cb44
> # Parent  445472225ccd07c27323cab59c4a1bf728f76142
> filemerge: add non-interactive internal:merge-local and internal:merge-other
[snip]
> +  $ hg up -C
> +  2 files updated, 0 files merged, 0 files removed, 0 files unresolved
> +  $ echo "\n\nEnd of file" >> a
> +  $ hg ci -m "Add some stuff at the end"
> +  $ hg up -r 1
> +  1 files updated, 0 files merged, 1 files removed, 0 files unresolved
> +  $ echo "Start of file\n\n" > tmp

I noticed echo \n breaks tests. Sorry, I should have run the full test
suite before sending. I will amend and resend.

- Jordi G. H.

Patch

diff --git a/mercurial/filemerge.py b/mercurial/filemerge.py
--- a/mercurial/filemerge.py
+++ b/mercurial/filemerge.py
@@ -229,6 +229,40 @@  def _imerge(repo, mynode, orig, fcd, fco
         return True, r
     return False, 0
 
+def _imergeauto(repo, mynode, orig, fcd, fco, fca, toolconf, files,
+                labels=None, localorother=None):
+    """
+    Generic driver for _imergelocal and _imergeother
+    """
+    assert localorother is not None
+    tool, toolpath, binary, symlink = toolconf
+    if symlink:
+        repo.ui.warn(_('warning: internal:merge-%s cannot merge symlinks '
+                       'for %s\n') % (localorother, fcd.path()))
+        return False, 1
+    a, b, c, back = files
+    r = simplemerge.simplemerge(repo.ui, a, b, c, label=labels,
+                                localorother=localorother)
+    return True, r
+
+@internaltool('merge-local', True)
+def _imergelocal(*args, **kwargs):
+    """
+    Like internal:merge, but resolve all conflicts non-interactively
+    in favor of the local changes.
+    """
+    success, status = _imergeauto(localorother='local', *args, **kwargs)
+    return success, status
+
+@internaltool('merge-other', True)
+def _imergelocal(*args, **kwargs):
+    """
+    Like internal:merge, but resolve all conflicts non-interactively
+    in favor of the other changes.
+    """
+    success, status = _imergeauto(localorother='other', *args, **kwargs)
+    return success, status
+
 @internaltool('merge3', True,
               _("merging %s incomplete! "
                 "(edit conflicts, then use 'hg resolve --mark')\n"))
diff --git a/mercurial/simplemerge.py b/mercurial/simplemerge.py
--- a/mercurial/simplemerge.py
+++ b/mercurial/simplemerge.py
@@ -82,7 +82,8 @@  class Merge3Text(object):
                     start_marker='<<<<<<<',
                     mid_marker='=======',
                     end_marker='>>>>>>>',
-                    base_marker=None):
+                    base_marker=None,
+                    localorother=None):
         """Return merge in cvs-like form.
         """
         self.conflicts = False
@@ -111,18 +112,25 @@  class Merge3Text(object):
                 for i in range(t[1], t[2]):
                     yield self.b[i]
             elif what == 'conflict':
-                self.conflicts = True
-                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
-                for i in range(t[5], t[6]):
-                    yield self.b[i]
-                yield end_marker + newline
+                if localorother == 'local':
+                    for i in range(t[3], t[4]):
+                        yield self.a[i]
+                elif localorother == 'other':
+                    for i in range(t[5], t[6]):
+                        yield self.b[i]
+                else:
+                    self.conflicts = True
+                    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
+                    for i in range(t[5], t[6]):
+                        yield self.b[i]
+                    yield end_marker + newline
             else:
                 raise ValueError(what)
 
@@ -373,7 +381,7 @@  def simplemerge(ui, local, base, other, 
         out = sys.stdout
 
     m3 = Merge3Text(basetext, localtext, othertext)
-    extrakwargs = {}
+    extrakwargs = {"localorother": opts.get("localorother", None)}
     if name_base is not None:
         extrakwargs['base_marker'] = '|||||||'
         extrakwargs['name_base'] = name_base
diff --git a/tests/test-conflict.t b/tests/test-conflict.t
--- a/tests/test-conflict.t
+++ b/tests/test-conflict.t
@@ -232,3 +232,68 @@  internal:merge3
   5
   >>>>>>> other
   Hop we are done.
+
+
+Add some unconflicting changes on each head, to make sure we really
+are merging, unlike internal:local and internal:other
+
+  $ hg up -C
+  2 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  $ echo "\n\nEnd of file" >> a
+  $ hg ci -m "Add some stuff at the end"
+  $ hg up -r 1
+  1 files updated, 0 files merged, 1 files removed, 0 files unresolved
+  $ echo "Start of file\n\n" > tmp
+  $ cat a >> tmp
+  $ mv tmp a
+  $ hg ci -m "Add some stuff at the beginning"
+
+Now test internal:merge-other and internal:merge-local
+
+  $ hg up -C
+  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  $ hg merge
+  merging a
+  warning: conflicts during merge.
+  merging a incomplete! (edit conflicts, then use 'hg resolve --mark')
+  1 files updated, 0 files merged, 0 files removed, 1 files unresolved
+  use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
+  [1]
+  $ hg resolve --tool internal:merge-other a
+  merging a
+  (no more unresolved files)
+  $ cat a
+  Start of file
+  
+  
+  Small Mathematical Series.
+  1
+  2
+  3
+  6
+  8
+  Hop we are done.
+  
+  
+  End of file
+
+  $ hg up -C
+  1 files updated, 0 files merged, 1 files removed, 0 files unresolved
+  $ hg merge --tool internal:merge-local
+  merging a
+  2 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  (branch merge, don't forget to commit)
+  $ cat a
+  Start of file
+  
+  
+  Small Mathematical Series.
+  1
+  2
+  3
+  4
+  5
+  Hop we are done.
+  
+  
+  End of file