| #!/usr/bin/env python |
| # Copyright 2017 the V8 project authors. All rights reserved. |
| # Use of this source code is governed by a BSD-style license that can be |
| # found in the LICENSE file. |
| |
| """ |
| Use this script to cherry-pick a V8 commit to backport to a Node.js checkout. |
| |
| Requirements: |
| - Node.js checkout to backport to. |
| - V8 checkout that contains the commit to cherry-pick. |
| |
| Usage: |
| $ backport_node.py <path_to_v8> <path_to_node> <commit-hash> |
| |
| This will apply the commit to <path_to_node>/deps/v8 and create a commit in |
| the Node.js checkout, increment patch level, and copy over the original |
| commit message. |
| |
| Optional flags: |
| --no-review Run `gclient sync` on the V8 checkout before updating. |
| """ |
| |
| import argparse |
| import os |
| import subprocess |
| import re |
| import sys |
| |
| TARGET_SUBDIR = os.path.join("deps", "v8") |
| VERSION_FILE = os.path.join("include", "v8-version.h") |
| VERSION_PATTERN = r'(?<=#define V8_PATCH_LEVEL )\d+' |
| |
| def FileToText(file_name): |
| with open(file_name) as f: |
| return f.read() |
| |
| def TextToFile(text, file_name): |
| with open(file_name, "w") as f: |
| f.write(text) |
| |
| |
| def Clean(options): |
| print ">> Cleaning target directory." |
| subprocess.check_call(["git", "clean", "-fd"], |
| cwd = os.path.join(options.node_path, TARGET_SUBDIR)) |
| |
| def CherryPick(options): |
| print ">> Apply patch." |
| patch = subprocess.Popen(["git", "diff-tree", "-p", options.commit], |
| stdout=subprocess.PIPE, cwd=options.v8_path) |
| patch.wait() |
| try: |
| subprocess.check_output(["git", "apply", "-3", "--directory=%s" % TARGET_SUBDIR], |
| stdin=patch.stdout, cwd=options.node_path) |
| except: |
| print ">> In another shell, please resolve patch conflicts" |
| print ">> and `git add` affected files." |
| print ">> Finally continue by entering RESOLVED." |
| while raw_input("[RESOLVED]") != "RESOLVED": |
| print ">> You need to type RESOLVED" |
| |
| def UpdateVersion(options): |
| print ">> Increment patch level." |
| version_file = os.path.join(options.node_path, TARGET_SUBDIR, VERSION_FILE) |
| text = FileToText(version_file) |
| def increment(match): |
| patch = int(match.group(0)) |
| return str(patch + 1) |
| text = re.sub(VERSION_PATTERN, increment, text, flags=re.MULTILINE) |
| TextToFile(text, version_file) |
| |
| def CreateCommit(options): |
| print ">> Creating commit." |
| # Find short hash from source. |
| shorthash = subprocess.check_output( |
| ["git", "rev-parse", "--short", options.commit], |
| cwd=options.v8_path).strip() |
| |
| # Commit message |
| title = "deps: backport %s from upstream V8" % shorthash |
| body = subprocess.check_output( |
| ["git", "log", options.commit, "-1", "--format=%B"], |
| cwd=options.v8_path).strip() |
| body = '\n'.join(" " + line for line in body.splitlines()) |
| |
| message = title + "\n\nOriginal commit message:\n\n" + body |
| |
| # Create commit at target. |
| review_message = "--no-edit" if options.no_review else "--edit" |
| git_commands = [ |
| ["git", "checkout", "-b", "backport_%s" % shorthash], # new branch |
| ["git", "add", TARGET_SUBDIR], # add files |
| ["git", "commit", "-m", message, review_message] # new commit |
| ] |
| for command in git_commands: |
| subprocess.check_call(command, cwd=options.node_path) |
| |
| def ParseOptions(args): |
| parser = argparse.ArgumentParser(description="Backport V8 commit to Node.js") |
| parser.add_argument("v8_path", help="Path to V8 checkout") |
| parser.add_argument("node_path", help="Path to Node.js checkout") |
| parser.add_argument("commit", help="Commit to backport") |
| parser.add_argument("--no-review", action="store_true", |
| help="Skip editing commit message") |
| options = parser.parse_args(args) |
| options.v8_path = os.path.abspath(options.v8_path) |
| assert os.path.isdir(options.v8_path) |
| options.node_path = os.path.abspath(options.node_path) |
| assert os.path.isdir(options.node_path) |
| return options |
| |
| def Main(args): |
| options = ParseOptions(args) |
| Clean(options) |
| try: |
| CherryPick(options) |
| UpdateVersion(options) |
| CreateCommit(options) |
| except: |
| print ">> Failed. Resetting." |
| subprocess.check_output(["git", "reset", "--hard"], cwd=options.node_path) |
| raise |
| |
| if __name__ == "__main__": |
| Main(sys.argv[1:]) |