Commit bc802709 authored by Kai Willadsen's avatar Kai Willadsen

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.
parent d634dca1
......@@ -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)
......@@ -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":
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment