]> scripts.mit.edu Git - wizard.git/blobdiff - wizard/resolve.py
Rewrite parametrize to use new parametrizeWithVars
[wizard.git] / wizard / resolve.py
index 9111203ec5d60c06fce10e2f97a29396a4dcc290..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,6 +109,10 @@ 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)
     regex = re.compile(rstring, re.DOTALL)
     repl = result_to_repl(result, mappings)
@@ -105,10 +122,12 @@ def resolve(contents, spec, result):
     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)
@@ -120,5 +139,9 @@ def resolve(contents, spec, result):
     return ret
 
 def is_conflict(contents):
+    """
+    Given ``contents``, return ``True`` if there are any conflicts in it.
+    """
     # Really really simple heuristic
     return "<<<<<<<" in contents
+