From: Edward Z. Yang Date: Sat, 23 Jul 2011 09:24:17 +0000 (-0400) Subject: Axe cat-file for reset --hard. X-Git-Url: https://scripts.mit.edu/gitweb/wizard.git/commitdiff_plain/ee81a755e902dc95fafa440f1c40b3dd8a703478 Axe cat-file for reset --hard. Signed-off-by: Edward Z. Yang --- diff --git a/wizard/merge.py b/wizard/merge.py index 0401268..b4d8b35 100644 --- a/wizard/merge.py +++ b/wizard/merge.py @@ -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,30 +124,51 @@ 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 + 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 @@ -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) - 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 @@ -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) - 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 +222,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)