11cb0ef41Sopenharmony_ci#!/usr/bin/env python 21cb0ef41Sopenharmony_ci# Copyright 2014 the V8 project authors. All rights reserved. 31cb0ef41Sopenharmony_ci# Use of this source code is governed by a BSD-style license that can be 41cb0ef41Sopenharmony_ci# found in the LICENSE file. 51cb0ef41Sopenharmony_ci 61cb0ef41Sopenharmony_ci# for py2/py3 compatibility 71cb0ef41Sopenharmony_cifrom __future__ import print_function 81cb0ef41Sopenharmony_ci 91cb0ef41Sopenharmony_ciimport argparse 101cb0ef41Sopenharmony_ciimport subprocess 111cb0ef41Sopenharmony_ciimport sys 121cb0ef41Sopenharmony_ci 131cb0ef41Sopenharmony_ci 141cb0ef41Sopenharmony_cidef GetArgs(): 151cb0ef41Sopenharmony_ci parser = argparse.ArgumentParser( 161cb0ef41Sopenharmony_ci description="Finds a commit that a given patch can be applied to. " 171cb0ef41Sopenharmony_ci "Does not actually apply the patch or modify your checkout " 181cb0ef41Sopenharmony_ci "in any way.") 191cb0ef41Sopenharmony_ci parser.add_argument("patch_file", help="Patch file to match") 201cb0ef41Sopenharmony_ci parser.add_argument( 211cb0ef41Sopenharmony_ci "--branch", "-b", default="origin/master", type=str, 221cb0ef41Sopenharmony_ci help="Git tree-ish where to start searching for commits, " 231cb0ef41Sopenharmony_ci "default: %(default)s") 241cb0ef41Sopenharmony_ci parser.add_argument( 251cb0ef41Sopenharmony_ci "--limit", "-l", default=500, type=int, 261cb0ef41Sopenharmony_ci help="Maximum number of commits to search, default: %(default)s") 271cb0ef41Sopenharmony_ci parser.add_argument( 281cb0ef41Sopenharmony_ci "--verbose", "-v", default=False, action="store_true", 291cb0ef41Sopenharmony_ci help="Print verbose output for your entertainment") 301cb0ef41Sopenharmony_ci return parser.parse_args() 311cb0ef41Sopenharmony_ci 321cb0ef41Sopenharmony_ci 331cb0ef41Sopenharmony_cidef FindFilesInPatch(patch_file): 341cb0ef41Sopenharmony_ci files = {} 351cb0ef41Sopenharmony_ci next_file = "" 361cb0ef41Sopenharmony_ci with open(patch_file) as patch: 371cb0ef41Sopenharmony_ci for line in patch: 381cb0ef41Sopenharmony_ci if line.startswith("diff --git "): 391cb0ef41Sopenharmony_ci # diff --git a/src/objects.cc b/src/objects.cc 401cb0ef41Sopenharmony_ci words = line.split() 411cb0ef41Sopenharmony_ci assert words[2].startswith("a/") and len(words[2]) > 2 421cb0ef41Sopenharmony_ci next_file = words[2][2:] 431cb0ef41Sopenharmony_ci elif line.startswith("index "): 441cb0ef41Sopenharmony_ci # index add3e61..d1bbf6a 100644 451cb0ef41Sopenharmony_ci hashes = line.split()[1] 461cb0ef41Sopenharmony_ci old_hash = hashes.split("..")[0] 471cb0ef41Sopenharmony_ci if old_hash.startswith("0000000"): continue # Ignore new files. 481cb0ef41Sopenharmony_ci files[next_file] = old_hash 491cb0ef41Sopenharmony_ci return files 501cb0ef41Sopenharmony_ci 511cb0ef41Sopenharmony_ci 521cb0ef41Sopenharmony_cidef GetGitCommitHash(treeish): 531cb0ef41Sopenharmony_ci cmd = ["git", "log", "-1", "--format=%H", treeish] 541cb0ef41Sopenharmony_ci return subprocess.check_output(cmd).strip() 551cb0ef41Sopenharmony_ci 561cb0ef41Sopenharmony_ci 571cb0ef41Sopenharmony_cidef CountMatchingFiles(commit, files): 581cb0ef41Sopenharmony_ci matched_files = 0 591cb0ef41Sopenharmony_ci # Calling out to git once and parsing the result Python-side is faster 601cb0ef41Sopenharmony_ci # than calling 'git ls-tree' for every file. 611cb0ef41Sopenharmony_ci cmd = ["git", "ls-tree", "-r", commit] + [f for f in files] 621cb0ef41Sopenharmony_ci output = subprocess.check_output(cmd) 631cb0ef41Sopenharmony_ci for line in output.splitlines(): 641cb0ef41Sopenharmony_ci # 100644 blob c6d5daaa7d42e49a653f9861224aad0a0244b944 src/objects.cc 651cb0ef41Sopenharmony_ci _, _, actual_hash, filename = line.split() 661cb0ef41Sopenharmony_ci expected_hash = files[filename] 671cb0ef41Sopenharmony_ci if actual_hash.startswith(expected_hash): matched_files += 1 681cb0ef41Sopenharmony_ci return matched_files 691cb0ef41Sopenharmony_ci 701cb0ef41Sopenharmony_ci 711cb0ef41Sopenharmony_cidef FindFirstMatchingCommit(start, files, limit, verbose): 721cb0ef41Sopenharmony_ci commit = GetGitCommitHash(start) 731cb0ef41Sopenharmony_ci num_files = len(files) 741cb0ef41Sopenharmony_ci if verbose: print(">>> Found %d files modified by patch." % num_files) 751cb0ef41Sopenharmony_ci for _ in range(limit): 761cb0ef41Sopenharmony_ci matched_files = CountMatchingFiles(commit, files) 771cb0ef41Sopenharmony_ci if verbose: print("Commit %s matched %d files" % (commit, matched_files)) 781cb0ef41Sopenharmony_ci if matched_files == num_files: 791cb0ef41Sopenharmony_ci return commit 801cb0ef41Sopenharmony_ci commit = GetGitCommitHash("%s^" % commit) 811cb0ef41Sopenharmony_ci print("Sorry, no matching commit found. " 821cb0ef41Sopenharmony_ci "Try running 'git fetch', specifying the correct --branch, " 831cb0ef41Sopenharmony_ci "and/or setting a higher --limit.") 841cb0ef41Sopenharmony_ci sys.exit(1) 851cb0ef41Sopenharmony_ci 861cb0ef41Sopenharmony_ci 871cb0ef41Sopenharmony_ciif __name__ == "__main__": 881cb0ef41Sopenharmony_ci args = GetArgs() 891cb0ef41Sopenharmony_ci files = FindFilesInPatch(args.patch_file) 901cb0ef41Sopenharmony_ci commit = FindFirstMatchingCommit(args.branch, files, args.limit, args.verbose) 911cb0ef41Sopenharmony_ci if args.verbose: 921cb0ef41Sopenharmony_ci print(">>> Matching commit: %s" % commit) 931cb0ef41Sopenharmony_ci print(subprocess.check_output(["git", "log", "-1", commit])) 941cb0ef41Sopenharmony_ci print(">>> Kthxbai.") 951cb0ef41Sopenharmony_ci else: 961cb0ef41Sopenharmony_ci print(commit) 97