Add an interractive 2-way merge option
authorCatalin Marinas <catalin.marinas@gmail.com>
Wed, 28 Feb 2007 08:41:56 +0000 (08:41 +0000)
committerCatalin Marinas <catalin.marinas@gmail.com>
Wed, 28 Feb 2007 08:41:56 +0000 (08:41 +0000)
Since emacs can handle 2-way merges as well with ediff-merge-files,
this patch changes the 'imerger' config option to 'i3merge' and also
adds an 'i2merge' option. The gitmergeonefile.py algorithm was
modified to handle 2-way merges interactively as well.

Signed-off-by: Catalin Marinas <catalin.marinas@gmail.com>
examples/gitconfig
stgit/gitmergeonefile.py

index 637f153..bbb943f 100644 (file)
        #       \"%(branch1)s\" \"%(ancestor)s\" \"%(branch2)s\" \
        #       > \"%(output)s\"
 
-       # Interactive three-way merge tool. It is executed by the 'resolved
-       # --interactive' command
-       #imerger = xxdiff --title1 current --title2 ancestor --title3 patched \
+       # 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\"
-       #imerger = emacs --eval '(ediff-merge-files-with-ancestor \
+       #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
        #autoimerge = no
index b9d02aa..3a55195 100644 (file)
@@ -102,29 +102,41 @@ def interactive_merge(filename):
     """Run the interactive merger on the given file. Note that the
     index should not have any conflicts.
     """
-    imerger = config.get('stgit.imerger')
-    if not imerger:
-        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 os.path.isfile(ancestor):
+        three_way = True
+        files_dict = {'branch1': current,
+                      'ancestor': ancestor,
+                      'branch2': patched,
+                      'output': filename}
+        imerger = config.get('stgit.i3merge')
+    else:
+        three_way = False
+        files_dict = {'branch1': current,
+                      'branch2': patched,
+                      'output': filename}
+        imerger = config.get('stgit.i2merge')
+
+    if not imerger:
+        raise GitMergeException, 'No interactive merge command configured'
+
+    # check whether we have all the files for the merge
+    for fn in [filename, current, patched]:
         if not os.path.isfile(fn):
             raise GitMergeException, \
-                  'Cannot run the interactive merger: "%s" missing' % fn
+                  'Cannot run the interactive merge: "%s" missing' % fn
 
     mtime = os.path.getmtime(filename)
 
-    err = os.system(imerger % {'branch1': current,
-                               'ancestor': ancestor,
-                               'branch2': patched,
-                               'output': filename})
+    print 'Trying the interractive %s merge' % \
+          {True: 'three-way', False: 'two-way'}[three_way]
 
+    err = os.system(imerger % files_dict)
     if err != 0:
         raise GitMergeException, 'The interactive merge failed: %d' % err
     if not os.path.isfile(filename):
@@ -185,8 +197,6 @@ def merge(orig_hash, file1_hash, file2_hash,
                               % (file1_mode, file1_hash, path))
 
                     if config.get('stgit.autoimerge') == 'yes':
-                        print >> sys.stderr, \
-                              'Trying the interactive merge'
                         try:
                             interactive_merge(path)
                         except GitMergeException, ex:
@@ -263,14 +273,35 @@ def merge(orig_hash, file1_hash, file2_hash,
                           'permissions conflict' % path
                     __conflict(path)
                     return 1
-            # files are different
+            # files added in both but different
             else:
-                # reset the index to the current file
-                os.system('git-update-index -- %s' % path)
                 print >> sys.stderr, \
                       'Error: File "%s" added in branches but different' % path
-                __conflict(path)
-                return 1
+                # reset the cache to the first branch
+                os.system('git-update-index --cacheinfo %s %s %s'
+                          % (file1_mode, file1_hash, path))
+
+                if config.get('stgit.autoimerge') == 'yes':
+                    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
+                    os.system('git-update-index -- %s' % path)
+                    __remove_files(orig_hash, file1_hash, file2_hash)
+                    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 added in one
         elif file1_hash or file2_hash:
             if file1_hash: