]> scripts.mit.edu Git - wizard.git/commitdiff
Axe cat-file for reset --hard.
authorEdward Z. Yang <ezyang@mit.edu>
Sat, 23 Jul 2011 09:24:17 +0000 (05:24 -0400)
committerEdward Z. Yang <ezyang@mit.edu>
Sat, 23 Jul 2011 09:24:17 +0000 (05:24 -0400)
Signed-off-by: Edward Z. Yang <ezyang@mit.edu>
wizard/merge.py

index 04012683367a00667ae749c26ad043131b3161e3..b4d8b35bd3f14754a2fd616e07fac2131af66949 100644 (file)
@@ -71,19 +71,6 @@ def git_diff_text(a, b):
             files.append(name)
     return files
 
             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):
     """
 # only works on Unix
 def get_newline(filename):
     """
@@ -137,30 +124,51 @@ def merge(common_id, theirs_id,
         so we require the common and theirs commits, instead of
         using the normal Git algorithm.
     """
         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 prepare_config is None:
         prepare_config = lambda: None
+
     if resolve_conflicts is None:
         resolve_conflicts = lambda: False
     if resolve_conflicts is None:
         resolve_conflicts = lambda: False
+
     ours_id = shell.eval("git", "rev-parse", "HEAD")
     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
+
     theirs_tree = shell.eval("git", "rev-parse", "%s^{tree}" % theirs_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:
         try:
-            theirs_nl = get_theirs_newline(file)
             ours_nl = get_newline(file) # current checkout is ours_id
         except (IOError, shell.CallError): # hack
             continue
             ours_nl = get_newline(file) # current checkout is ours_id
         except (IOError, shell.CallError): # hack
             continue
@@ -170,15 +178,21 @@ def merge(common_id, theirs_id,
             else:
                 logging.info("Converting our file (3) from %s to %s newlines", repr(ours_nl), repr(theirs_nl))
                 convert_newline(file, theirs_nl)
             else:
                 logging.info("Converting our file (3) from %s to %s newlines", 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)
     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:
     # 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
             common_nl = get_newline(file) # current checkout is common_id
         except (IOError, shell.CallError): # hack
             continue
@@ -188,13 +202,15 @@ def merge(common_id, theirs_id,
             else:
                 logging.info("Converting common file (1) from %s to %s newlines", repr(common_nl), repr(theirs_nl))
                 convert_newline(file, theirs_nl)
             else:
                 logging.info("Converting common file (1) from %s to %s newlines", 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)
     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)
     # 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:
     # perform merge
     shell.call("git", "reset", "--hard", ours_virtual_id)
     try:
@@ -206,6 +222,7 @@ def merge(common_id, theirs_id,
             shell.call("git", "commit", "-a", "-m", "merge")
         else:
             raise MergeError
             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)
     # post-merge operations
     result_tree = shell.eval("git", "write-tree")
     logging.info("Resolution tree is %s", result_tree)