]> scripts.mit.edu Git - wizard.git/blobdiff - wizard/merge.py
Fix bug where php.ini not being rewritten for MediaWiki.
[wizard.git] / wizard / merge.py
index 04012683367a00667ae749c26ad043131b3161e3..1b99a7a664baa284faa8c55abac3600a01b30cf6 100644 (file)
@@ -71,19 +71,6 @@ def git_diff_text(a, b):
             files.append(name)
     return files
 
-def git_newline_style(rev, name):
-    """
-    Returns the newline style for a blob, identified by Git revision
-    ``rev`` and filename ``name``.
-    """
-    # XXX This is really expensive
-    f = tempfile.NamedTemporaryFile(prefix="wizardResolve", delete=False)
-    shell.call("git", "cat-file", "blob", "%s:%s" % (rev, name), stdout=f, log=False)
-    f.close()
-    nl = get_newline(f.name)
-    os.unlink(f.name)
-    return nl
-
 # only works on Unix
 def get_newline(filename):
     """
@@ -137,64 +124,94 @@ def merge(common_id, theirs_id,
         so we require the common and theirs commits, instead of
         using the normal Git algorithm.
     """
+
     if prepare_config is None:
         prepare_config = lambda: None
+
     if resolve_conflicts is None:
         resolve_conflicts = lambda: False
+
     ours_id = shell.eval("git", "rev-parse", "HEAD")
-    theirs_newline_cache = {}
-    def get_theirs_newline(file):
-        if file not in theirs_newline_cache:
-            nl = git_newline_style(theirs_id, file)
-            if not isinstance(nl, str):
-                if nl is not None:
-                    # A case of incompetent upstream, unfortunately
-                    logging.warning("Canonical version (theirs) of %s has mixed newline style, forced to \\n", file)
-                else:
-                    logging.debug("Canonical version (theirs) of %s had no newline style, using \\n", file)
-                nl = "\n"
-            theirs_newline_cache[file] = nl
-        return theirs_newline_cache[file]
+    ours_theirs_diff  = git_diff_text(ours_id, theirs_id)
+
+    # What files can the merge fail on? Only if ours is different from
+    # theirs (we don't care about common for this calculation).  Of
+    # course, this will be conservative, because we need to apply
+    # prepare_config to ours.  Can we miss a file?  Not unless
+    # prepare_config introduces a merge conflict, as opposed to
+    # eliminates them; and it is equally likely to do so on common_id as
+    # well. We can deal, since we offer the user the ability to resolve
+    # merges  manually.
+    theirs_newlines = {}
+    shell.call("git", "reset", "--hard", theirs_id)
+    for file in ours_theirs_diff:
+        # XXX Should be able to skip stats if file was removed
+        # for the ours tree
+        try:
+            nl = get_newline(file)
+        except IOError:
+            # File not present in theirs, don't bother
+            continue
+        if not isinstance(nl, str):
+            if nl is not None:
+                # A case of incompetent upstream, unfortunately
+                logging.warning("Canonical version (theirs) of %s has mixed newline style, forced to \\n", file)
+            else:
+                logging.debug("Canonical version (theirs) of %s had no newline style, using \\n", file)
+            nl = "\n"
+        theirs_newlines[file] = nl
+
+    shell.call("git", "reset", "--hard", ours_id)
     theirs_tree = shell.eval("git", "rev-parse", "%s^{tree}" % theirs_id)
-    # XXX Should be able to skip stats if file doesn't exist
-    # operations on the ours tree
-    for file in git_diff_text(ours_id, theirs_id):
+    for file in ours_theirs_diff:
+        try:
+            theirs_nl = theirs_newlines[file]
+        except KeyError:
+            # No need to handle newlines
+            continue
         try:
-            theirs_nl = get_theirs_newline(file)
             ours_nl = get_newline(file) # current checkout is ours_id
         except (IOError, shell.CallError): # hack
             continue
         if theirs_nl != ours_nl:
             if ours_nl is None:
-                logging.debug("File had no newlines, ignoring newline style")
+                logging.debug("Our file %s had no newlines, ignoring newline style", file)
             else:
-                logging.info("Converting our file (3) from %s to %s newlines", repr(ours_nl), repr(theirs_nl))
+                logging.info("Converting our file %s (3) from %s to %s newlines", file, repr(ours_nl), repr(theirs_nl))
                 convert_newline(file, theirs_nl)
-                shell.eval("git", "add", file)
+                shell.eval("git", "add", file) # XXX batch this
     prepare_config() # for Wizard, this usually genericizes config files
     ours_tree = shell.eval("git", "write-tree")
     logging.info("Merge wrote virtual tree for ours: %s", ours_tree)
+
     # operations on the common tree (pretty duplicate with the above)
     shell.call("git", "reset", "--hard", common_id)
     for file in git_diff_text(common_id, theirs_id):
         try:
-            theirs_nl = get_theirs_newline(file)
+            theirs_nl = theirs_newlines[file]
+        except KeyError:
+            # The merge trivially succeeds, so don't bother.
+            logging.debug("Merge trivially succeeds for %s, ignoring line check", file)
+            continue
+        try:
             common_nl = get_newline(file) # current checkout is common_id
         except (IOError, shell.CallError): # hack
             continue
         if theirs_nl != common_nl:
             if common_nl is None:
-                logging.debug("File had no newlines, ignoring newline style")
+                logging.debug("Common file %s had no newlines, ignoring newline style", file)
             else:
-                logging.info("Converting common file (1) from %s to %s newlines", repr(common_nl), repr(theirs_nl))
+                logging.info("Converting common file %s (1) from %s to %s newlines", file, repr(common_nl), repr(theirs_nl))
                 convert_newline(file, theirs_nl)
-                shell.eval("git", "add", file)
+                shell.eval("git", "add", file) # XXX batch
     common_tree = shell.eval("git", "write-tree")
     logging.info("Merge wrote virtual tree for common: %s", common_tree)
+
     # construct merge commit graph
     common_virtual_id = git_commit_tree(common_tree)
     ours_virtual_id   = git_commit_tree(ours_tree, common_virtual_id)
     theirs_virtual_id = git_commit_tree(theirs_tree, common_virtual_id)
+
     # perform merge
     shell.call("git", "reset", "--hard", ours_virtual_id)
     try:
@@ -206,6 +223,7 @@ def merge(common_id, theirs_id,
             shell.call("git", "commit", "-a", "-m", "merge")
         else:
             raise MergeError
+
     # post-merge operations
     result_tree = shell.eval("git", "write-tree")
     logging.info("Resolution tree is %s", result_tree)