chiark / gitweb /
Add mergetool support to the classic StGit infrastructure
authorCatalin Marinas <catalin.marinas@arm.com>
Thu, 9 Apr 2009 20:40:58 +0000 (23:40 +0300)
committerCatalinMarinas <cmarinas@laptop.(none)>
Thu, 9 Apr 2009 20:40:58 +0000 (23:40 +0300)
Since Git already has a tool for interactively solving conflicts which
is highly customisable, there is no need to duplicate this feature via
the i3merge and i2merge configuration options. The user-visible change
is that now mergetool is invoked rather than the previously customised
interactive merging tool.

The stgit.keeporig option is no longer available to be more consistent
with the Git behaviour.

Signed-off-by: Catalin Marinas <catalin.marinas@gmail.com>
Acked-by: Karl Hasselström <kha@treskal.com>
examples/gitconfig
stgit/commands/resolved.py
stgit/config.py
stgit/git.py
stgit/gitmergeonefile.py [deleted file]
t/t0002-status.sh

index 2fc5f520d4dbcb92a206ac39de9d237c8d61e1c2..f6e3a798c5349169d05777717097f04570ff55e4 100644 (file)
        # To support local parent branches:
        #pull-policy = rebase
 
-       # Interactive two/three-way merge tool. It is executed by the
-       # 'resolved --interactive' command
-       #i3merge = xxdiff --title1 current --title2 ancestor --title3 patched \
-       #       --show-merged-pane -m -E -O -X -M \"%(output)s\" \
-       #       \"%(branch1)s\" \"%(ancestor)s\" \"%(branch2)s\"
-       #i2merge = xxdiff --title1 current --title2 patched \
-       #       --show-merged-pane -m -E -O -X -M \"%(output)s\" \
-       #       \"%(branch1)s\" \"%(branch2)s\"
-       #i3merge = emacs --eval '(ediff-merge-files-with-ancestor \
-       #       \"%(branch1)s\" \"%(branch2)s\" \"%(ancestor)s\" nil \
-       #       \"%(output)s\")'
-       #i2merge = emacs --eval '(ediff-merge-files \
-       #       \"%(branch1)s\" \"%(branch2)s\" nil \"%(output)s\")'
-
-       # Automatically invoke the interactive merger in case of conflicts
+       # Automatically invoke the interactive merger (git mergetool) in case
+       # of conflicts
        #autoimerge = no
 
-       # Leave the original files in the working tree in case of a
-       # merge conflict
-       #keeporig = yes
-
        # Optimize (repack) the object store after every pull
        #keepoptimized = yes
 
index 2ce7ec3a585c2a9d9555e694db2255a7595cebb1..eba778db4edcf7b166ebc9b1f0ef11857f9f898d 100644 (file)
@@ -22,7 +22,6 @@ from stgit.commands.common import *
 from stgit.utils import *
 from stgit import argparse, stack, git, basedir
 from stgit.config import config, file_extensions
-from stgit.gitmergeonefile import interactive_merge
 
 help = 'Mark a file conflict as solved'
 kind = 'wc'
@@ -78,8 +77,6 @@ def func(parser, options, args):
 
     # resolved
     if options.interactive:
-        for filename in files:
-            interactive_merge(filename)
-            git.resolved([filename])
+        git.mergetool(files)
     else:
         git.resolved(files, options.reset)
index 05ef624f41b4486d667958e9e8f7c9b91d01a86e..efce097508bf313277b2a5456a9e318bb422d2fd 100644 (file)
@@ -35,7 +35,6 @@ class GitConfig:
         'stgit.fetchcmd':      'git fetch',
         'stgit.pull-policy':   'pull',
         'stgit.autoimerge':    'no',
-        'stgit.keeporig':      'yes',
         'stgit.keepoptimized': 'no',
         'stgit.extensions':    '.ancestor .current .patched',
         'stgit.shortnr':        '5'
index 4d01fc296c389be4155249126cbeb556744d44bf..cbdefe8b27a6ac57537b70afa15de591223b1291 100644 (file)
@@ -18,7 +18,7 @@ along with this program; if not, write to the Free Software
 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 """
 
-import sys, os, re, gitmergeonefile
+import sys, os, re
 from shutil import copyfile
 
 from stgit.exception import *
@@ -632,19 +632,23 @@ def merge_recursive(base, head1, head2):
     output = p.output_lines()
     if p.exitcode:
         # There were conflicts
-        conflicts = [l.strip() for l in output if l.startswith('CONFLICT')]
+        if config.get('stgit.autoimerge') == 'yes':
+            mergetool()
+        else:
+            conflicts = [l for l in output if l.startswith('CONFLICT')]
+            out.info(*conflicts)
+            raise GitException, "%d conflict(s)" % len(conflicts)
+
+def mergetool(files = ()):
+    """Invoke 'git mergetool' to resolve any outstanding conflicts. If 'not
+    files', all the files in an unmerged state will be processed."""
+    GRun('mergetool', *list(files)).returns([0, 1]).run()
+    # check for unmerged entries (prepend 'CONFLICT ' for consistency with
+    # merge_recursive())
+    conflicts = ['CONFLICT ' + f for f in get_conflicts()]
+    if conflicts:
         out.info(*conflicts)
-
-        # try the interactive merge or stage checkout (if enabled)
-        for filename in get_conflicts():
-            if (gitmergeonefile.merge(filename)):
-                # interactive merge succeeded
-                resolved([filename])
-
-        # any conflicts left unsolved?
-        cn = len(get_conflicts())
-        if cn:
-            raise GitException, "%d conflict(s)" % cn
+        raise GitException, "%d conflict(s)" % len(conflicts)
 
 def diff(files = None, rev1 = 'HEAD', rev2 = None, diff_flags = [],
          binary = True):
@@ -754,7 +758,6 @@ def resolved(filenames, reset = None):
              '--stdin', '-z').input_nulterm(filenames).no_output()
     GRun('update-index', '--add', '--').xargs(filenames)
     for filename in filenames:
-        gitmergeonefile.clean_up(filename)
         # update the access and modificatied times
         os.utime(filename, None)
 
diff --git a/stgit/gitmergeonefile.py b/stgit/gitmergeonefile.py
deleted file mode 100644 (file)
index 1fe226e..0000000
+++ /dev/null
@@ -1,150 +0,0 @@
-"""Performs a 3-way merge for GIT files
-"""
-
-__copyright__ = """
-Copyright (C) 2006, Catalin Marinas <catalin.marinas@gmail.com>
-
-This program is free software; you can redistribute it and/or modify
-it under the terms of the GNU General Public License version 2 as
-published by the Free Software Foundation.
-
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with this program; if not, write to the Free Software
-Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
-"""
-
-import sys, os
-from stgit.exception import *
-from stgit import basedir
-from stgit.config import config, file_extensions, ConfigOption
-from stgit.utils import append_string
-from stgit.out import *
-from stgit.run import *
-
-class GitMergeException(StgException):
-    pass
-
-
-#
-# Options
-#
-autoimerge = ConfigOption('stgit', 'autoimerge')
-keeporig = ConfigOption('stgit', 'keeporig')
-
-#
-# Utility functions
-#
-def __str2none(x):
-    if x == '':
-        return None
-    else:
-        return x
-
-class MRun(Run):
-    exc = GitMergeException # use a custom exception class on errors
-
-def __checkout_stages(filename):
-    """Check-out the merge stages in the index for the give file
-    """
-    extensions = file_extensions()
-    line = MRun('git', 'checkout-index', '--stage=all', '--', filename
-                ).output_one_line()
-    stages, path = line.split('\t')
-    stages = dict(zip(['ancestor', 'current', 'patched'],
-                      stages.split(' ')))
-
-    for stage, fn in stages.iteritems():
-        if stages[stage] == '.':
-            stages[stage] = None
-        else:
-            newname = filename + extensions[stage]
-            if os.path.exists(newname):
-                # remove the stage if it is already checked out
-                os.remove(newname)
-            os.rename(stages[stage], newname)
-            stages[stage] = newname
-
-    return stages
-
-def __remove_stages(filename):
-    """Remove the merge stages from the working directory
-    """
-    extensions = file_extensions()
-    for ext in extensions.itervalues():
-        fn = filename + ext
-        if os.path.isfile(fn):
-            os.remove(fn)
-
-def interactive_merge(filename):
-    """Run the interactive merger on the given file. Stages will be
-    removed according to stgit.keeporig. If successful and stages
-    kept, they will be removed via git.resolved().
-    """
-    stages = __checkout_stages(filename)
-
-    try:
-        # Check whether we have all the files for the merge.
-        if not (stages['current'] and stages['patched']):
-            raise GitMergeException('Cannot run the interactive merge')
-
-        if stages['ancestor']:
-            three_way = True
-            files_dict = {'branch1': stages['current'],
-                          'ancestor': stages['ancestor'],
-                          'branch2': stages['patched'],
-                          'output': filename}
-            imerger = config.get('stgit.i3merge')
-        else:
-            three_way = False
-            files_dict = {'branch1': stages['current'],
-                          'branch2': stages['patched'],
-                          'output': filename}
-            imerger = config.get('stgit.i2merge')
-
-        if not imerger:
-            raise GitMergeException, 'No interactive merge command configured'
-
-        mtime = os.path.getmtime(filename)
-
-        out.start('Trying the interactive %s merge'
-                  % (three_way and 'three-way' or 'two-way'))
-        err = os.system(imerger % files_dict)
-        out.done()
-        if err != 0:
-            raise GitMergeException, 'The interactive merge failed'
-        if not os.path.isfile(filename):
-            raise GitMergeException, 'The "%s" file is missing' % filename
-        if mtime == os.path.getmtime(filename):
-            raise GitMergeException, 'The "%s" file was not modified' % filename
-    finally:
-        # keep the merge stages?
-        if str(keeporig) != 'yes':
-            __remove_stages(filename)
-
-def clean_up(filename):
-    """Remove merge conflict stages if they were generated.
-    """
-    if str(keeporig) == 'yes':
-        __remove_stages(filename)
-
-def merge(filename):
-    """Merge one file if interactive is allowed or check out the stages
-    if keeporig is set.
-    """
-    if str(autoimerge) == 'yes':
-        try:
-            interactive_merge(filename)
-        except GitMergeException, ex:
-            out.error(str(ex))
-            return False
-        return True
-
-    if str(keeporig) == 'yes':
-        __checkout_stages(filename)
-
-    return False
index ac92aa8a059a5273ec83a78a479ca4f5486137b3..d95a83b64e438adda1fe526d01d340d83d142455 100755 (executable)
@@ -107,9 +107,6 @@ test_expect_success 'Make a conflicting patch' '
 '
 
 cat > expected.txt <<EOF
-? foo/bar.ancestor
-? foo/bar.current
-? foo/bar.patched
 A fie
 C foo/bar
 EOF