The conflict resolution DSL is described here:
Resolutions are specified as input-output pairs. An input
-is a string with the conflict resolution markers ("<" * 7,
-"=" * 7 and ">" * 7), with the HEAD content above the equals
+is a string with the conflict resolution markers ``("<" * 7,
+"=" * 7 and ">" * 7)``, with the HEAD content above the equals
divider, and the upstream content below the equals divider.
-Lines can also be marked as "***N***" where N is a natural
+Lines can also be marked as ``***N***`` where N is a natural
number greater than 0 (i.e. 1 or more), which means that
an arbitrary number of lines may be matched and available for output.
<<<<<<<
downstream
+ |||||||
+ common
=======
upstream
>>>>>>>
import re
import itertools
+import logging
re_var = re.compile("^\*\*\*(\d+)\*\*\*\\\n", re.MULTILINE)
else:
ret += re.escape(line)
return ("(" + ret + ")", mappings)
- ours_regex, ours_mappings = regexify(ours, -1, 1)
- theirs_regex, theirs_mappings = regexify(theirs, 0, len(ours_mappings) + 1)
+ ours, split, common = ours.partition("|||||||\n")
+ if not split:
+ common = "***9999***\n" # force wildcard behavior
+ ours_regex, ours_mappings = regexify(ours, -1, 1)
+ common_regex, common_mappings = regexify(common, -2, 1 + len(ours_mappings))
+ theirs_regex, theirs_mappings = regexify(theirs, 0, 1 + len(ours_mappings) + len(common_mappings))
+ # unify the mappings
ours_mappings.update(theirs_mappings)
- return ("<<<<<<<[^\n]*\\\n" + ours_regex + "=======\\\n" + theirs_regex + ">>>>>>>[^\n]*(\\\n|$)", ours_mappings)
+ ours_mappings.update(common_mappings)
+ return ("<<<<<<<[^\n]*\\\n" + ours_regex + "\|\|\|\|\|\|\|\\\n" + common_regex + "=======\\\n" + theirs_regex + ">>>>>>>[^\n]*(\\\n|$)", ours_mappings)
def result_to_repl(result, mappings):
+ """
+ Converts a list of replacement strings and or references
+ into a replacement string appropriate for a regular expression.
+ """
def ritem_to_string(r):
if type(r) is int:
return "\\%d" % mappings[r]
return "".join(map(ritem_to_string, result))
def resolve(contents, spec, result):
+ """
+ Given a conflicted file, whose contents are ``contents``, attempt
+ to resolve all conflicts that match ``spec`` with ``result``.
+ """
rstring, mappings = spec_to_regex(spec)
regex = re.compile(rstring, re.DOTALL)
repl = result_to_repl(result, mappings)
for line in contents.splitlines(True):
if status == 0 and line.startswith("<<<<<<<"):
status = 1
- elif status == 1 and line.startswith("======="):
+ elif status == 1 and line.startswith("|||||||"):
status = 2
+ elif status == 1 or status == 2 and line.startswith("======="):
+ status = 3
# ok, now process
- if status == 2 and line.startswith(">>>>>>>"):
+ if status == 3 and line.startswith(">>>>>>>"):
status = 0
conflict += line
ret += regex.sub(repl, conflict)
return ret
def is_conflict(contents):
+ """
+ Given ``contents``, return ``True`` if there are any conflicts in it.
+ """
# Really really simple heuristic
return "<<<<<<<" in contents
+