]> scripts.mit.edu Git - wizard.git/blobdiff - wizard/resolve.py
Remove string exception from remaster.
[wizard.git] / wizard / resolve.py
index e2de1f217bce12bb16a0b41e33e73eabeae358b8..4460cc6d8ce57ccb9efa44035b33677b6f6c876e 100644 (file)
@@ -12,10 +12,10 @@ for resolving conflicts in configuration files.
 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.
 
@@ -33,6 +33,8 @@ Here are some examples::
 
     <<<<<<<
     downstream
+    |||||||
+    common
     =======
     upstream
     >>>>>>>
@@ -59,6 +61,7 @@ then the user matched globs.
 
 import re
 import itertools
+import logging
 
 re_var = re.compile("^\*\*\*(\d+)\*\*\*\\\n", re.MULTILINE)
 
@@ -82,12 +85,22 @@ def spec_to_regex(spec):
             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]
@@ -96,8 +109,39 @@ def result_to_repl(result, mappings):
     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)
-    print rstring
     regex = re.compile(rstring, re.DOTALL)
     repl = result_to_repl(result, mappings)
-    return regex.sub(repl, contents)
+    ret = ""
+    conflict = ""
+    status = 0
+    for line in contents.splitlines(True):
+        if status == 0 and line.startswith("<<<<<<<"):
+            status = 1
+        elif status == 1 and line.startswith("|||||||"):
+            status = 2
+        elif status == 1 or status == 2 and line.startswith("======="):
+            status = 3
+        # ok, now process
+        if status == 3 and line.startswith(">>>>>>>"):
+            status = 0
+            conflict += line
+            ret += regex.sub(repl, conflict)
+            conflict = ""
+        elif status:
+            conflict += line
+        else:
+            ret += line
+    return ret
+
+def is_conflict(contents):
+    """
+    Given ``contents``, return ``True`` if there are any conflicts in it.
+    """
+    # Really really simple heuristic
+    return "<<<<<<<" in contents
+