[meld] vc: Add support for showing BASE in Git conflicts



commit bc802709392a291d38cf8fd73180253311d40219
Author: Kai Willadsen <kai willadsen gmail com>
Date:   Sun May 26 07:59:37 2013 +1000

    vc: Add support for showing BASE in Git conflicts
    
    This commit uses Git plumbing to reconstruct a merged conflict file
    that includes diff3 conflict markers, and then extracts those markers
    to replace the traditional two- or three-way conflict with the BASE
    revision in the middle pane.
    
    The addition of the '==== BASE ====' string is, while ugly, necessary
    in order to actually guarantee some kind of conflict. Without a unique
    string there, it's entirely possible that the differences between the
    panes will show up as, for example, an insertion from LOCAL to BASE and
    a modification between BASE and REMOTE.

 meld/vc/_vc.py |   12 ++++++++++++
 meld/vc/git.py |   28 +++++++++++++++++++++++++++-
 2 files changed, 39 insertions(+), 1 deletions(-)
---
diff --git a/meld/vc/_vc.py b/meld/vc/_vc.py
index 6143707..49d21cf 100644
--- a/meld/vc/_vc.py
+++ b/meld/vc/_vc.py
@@ -25,6 +25,7 @@
 
 import itertools
 import os
+import re
 import subprocess
 
 from gi.repository import Gio
@@ -342,3 +343,14 @@ def popen(cmd, cwd=None):
 def call(cmd, cwd=None):
     NULL = open(os.devnull, "wb")
     return subprocess.call(cmd, cwd=cwd, stdout=NULL, stderr=NULL)
+
+
+base_re = re.compile(
+    r"^<{7}.*?$\r?\n(?P<local>.*?)"
+    r"^\|{7}.*?$\r?\n(?P<base>.*?)"
+    r"^={7}.*?$\r?\n(?P<remote>.*?)"
+    r"^>{7}.*?$\r?\n", flags=re.DOTALL | re.MULTILINE)
+
+
+def base_from_diff3(merged):
+    return base_re.sub(r"==== BASE ====\n\g<base>==== BASE ====\n", merged)
diff --git a/meld/vc/git.py b/meld/vc/git.py
index 8ef6c13..4aad25f 100644
--- a/meld/vc/git.py
+++ b/meld/vc/git.py
@@ -29,8 +29,10 @@ import errno
 import os
 import re
 import shutil
+import stat
 import subprocess
 import sys
+import StringIO
 import tempfile
 from collections import defaultdict
 
@@ -189,7 +191,31 @@ class Vc(_vc.Vc):
 
         if conflict == _vc.CONFLICT_MERGED:
             # Special case: no way to get merged result from git directly
-            return path, False
+            local, _ = self.get_path_for_conflict(path, _vc.CONFLICT_LOCAL)
+            base, _ = self.get_path_for_conflict(path, _vc.CONFLICT_BASE)
+            remote, _ = self.get_path_for_conflict(path, _vc.CONFLICT_REMOTE)
+
+            if not (local and base and remote):
+                raise _vc.InvalidVCPath(self, path,
+                                        "Couldn't access conflict parents")
+
+            args = [self.CMD, "merge-file", "-p", "--diff3", local, base,
+                    remote]
+            process = subprocess.Popen(args, cwd=self.location,
+                                       stdout=subprocess.PIPE)
+            vc_file = StringIO.StringIO(
+                _vc.base_from_diff3(process.stdout.read()))
+
+            prefix = 'meld-tmp-%s-' % _vc.conflicts[conflict]
+            with tempfile.NamedTemporaryFile(prefix=prefix, delete=False) as f:
+                shutil.copyfileobj(vc_file, f)
+
+            for temp_file in (local, base, remote):
+                if os.name == "nt":
+                    os.chmod(temp_file, stat.S_IWRITE)
+                os.remove(temp_file)
+
+            return f.name, True
 
         path = path[len(self.root) + 1:]
         if os.name == "nt":


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]