Add option to automatically invoke the interactive merger
[stgit] / stgit / gitmergeonefile.py
index b35880e..200448b 100644 (file)
@@ -20,7 +20,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 
 import sys, os
 from stgit import basedir
-from stgit.config import file_extensions, ConfigOption
+from stgit.config import config, file_extensions, ConfigOption
 from stgit.utils import append_string
 
 
@@ -75,6 +75,13 @@ def __checkout_files(orig_hash, file1_hash, file2_hash,
         os.chmod(tmp, int(file2_mode, 8))
         os.renames(tmp, src2)
 
+    if file1_hash and not os.path.exists(path):
+        # the current file might be removed by GIT when it is a new
+        # file added in both branches. Just re-generate it
+        tmp = __output('git-unpack-file %s' % file1_hash)
+        os.chmod(tmp, int(file1_mode, 8))
+        os.renames(tmp, path)
+
 def __remove_files(orig_hash, file1_hash, file2_hash):
     """Remove any temporary files
     """
@@ -84,7 +91,6 @@ def __remove_files(orig_hash, file1_hash, file2_hash):
         os.remove(src1)
     if file2_hash:
         os.remove(src2)
-    pass
 
 def __conflict(path):
     """Write the conflict file for the 'path' variable and exit
@@ -92,6 +98,42 @@ def __conflict(path):
     append_string(os.path.join(basedir.get(), 'conflicts'), path)
 
 
+def interactive_merge(filename):
+    """Run the interactive merger on the given file. Note that the
+    index should not have any conflicts.
+    """
+    try:
+        imerger = config.get('stgit', 'imerger')
+    except Exception, err:
+        raise GitMergeException, 'Configuration error: %s' % err
+
+    extensions = file_extensions()
+
+    ancestor = filename + extensions['ancestor']
+    current = filename + extensions['current']
+    patched = filename + extensions['patched']
+
+    # check whether we have all the files for a three-way merge
+    for fn in [filename, ancestor, current, patched]:
+        if not os.path.isfile(fn):
+            raise GitMergeException, \
+                  'Cannot run the interactive merger: "%s" missing' % fn
+
+    mtime = os.path.getmtime(filename)
+
+    err = os.system(imerger % {'branch1': current,
+                               'ancestor': ancestor,
+                               'branch2': patched,
+                               'output': filename})
+
+    if err != 0:
+        raise GitMergeException, 'The interactive merge failed: %d' % err
+    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
+
+
 #
 # Main algorithm
 #
@@ -142,10 +184,30 @@ def merge(orig_hash, file1_hash, file2_hash,
                     # reset the cache to the first branch
                     os.system('git-update-index --cacheinfo %s %s %s'
                               % (file1_mode, file1_hash, path))
-                    if str(keeporig) != 'yes':
+
+                    if config.get('stgit', 'autoimerge') == 'yes':
+                        print >> sys.stderr, \
+                              'Trying the interactive merge'
+                        try:
+                            interactive_merge(path)
+                        except GitMergeException, ex:
+                            # interactive merge failed
+                            print >> sys.stderr, str(ex)
+                            if str(keeporig) != 'yes':
+                                __remove_files(orig_hash, file1_hash,
+                                               file2_hash)
+                            __conflict(path)
+                            return 1
+                        # successful interactive merge
                         __remove_files(orig_hash, file1_hash, file2_hash)
-                    __conflict(path)
-                    return 1
+                        return 0
+                    else:
+                        # no interactive merge, just mark it as conflict
+                        if str(keeporig) != 'yes':
+                            __remove_files(orig_hash, file1_hash, file2_hash)
+                        __conflict(path)
+                        return 1
+
         # file deleted in both or deleted in one and unchanged in the other
         elif not (file1_hash or file2_hash) \
                or file1_hash == orig_hash or file2_hash == orig_hash: