OLD | NEW |
(Empty) | |
| 1 #!/usr/bin/env python |
| 2 # Copyright 2015 the V8 project authors. All rights reserved. |
| 3 # Use of this source code is governed by a BSD-style license that can be |
| 4 # found in the LICENSE file. |
| 5 |
| 6 import argparse |
| 7 import operator |
| 8 import os |
| 9 import re |
| 10 from sets import Set |
| 11 from subprocess import Popen, PIPE |
| 12 import sys |
| 13 |
| 14 def search_all_related_commits( |
| 15 git_working_dir, start_hash, until, separator, verbose=False): |
| 16 |
| 17 all_commits_raw = _find_commits_inbetween( |
| 18 start_hash, until, git_working_dir, verbose) |
| 19 if verbose: |
| 20 print "All commits between <of> and <until>: " + all_commits_raw |
| 21 |
| 22 # Adding start hash too |
| 23 all_commits = [start_hash] |
| 24 all_commits.extend(all_commits_raw.splitlines()) |
| 25 all_related_commits = {} |
| 26 already_treated_commits = Set([]) |
| 27 for commit in all_commits: |
| 28 if commit in already_treated_commits: |
| 29 continue |
| 30 |
| 31 related_commits = _search_related_commits( |
| 32 git_working_dir, commit, until, separator, verbose) |
| 33 if len(related_commits) > 0: |
| 34 all_related_commits[commit] = related_commits |
| 35 already_treated_commits.update(related_commits) |
| 36 |
| 37 already_treated_commits.update(commit) |
| 38 |
| 39 return all_related_commits |
| 40 |
| 41 def _search_related_commits( |
| 42 git_working_dir, start_hash, until, separator, verbose=False): |
| 43 |
| 44 if separator: |
| 45 commits_between = _find_commits_inbetween( |
| 46 start_hash, separator, git_working_dir, verbose) |
| 47 if commits_between == "": |
| 48 return [] |
| 49 |
| 50 # Extract commit position |
| 51 original_message = _git_execute( |
| 52 git_working_dir, |
| 53 ["show", "-s", "--format=%B", start_hash], |
| 54 verbose) |
| 55 title = original_message.splitlines()[0] |
| 56 |
| 57 matches = re.search("(\{#)([0-9]*)(\})", original_message) |
| 58 |
| 59 if not matches: |
| 60 return [] |
| 61 |
| 62 commit_position = matches.group(2) |
| 63 if verbose: |
| 64 print "1.) Commit position to look for: " + commit_position |
| 65 |
| 66 search_range = start_hash + ".." + until |
| 67 |
| 68 def git_args(grep_pattern): |
| 69 return [ |
| 70 "log", |
| 71 "--reverse", |
| 72 "--grep=" + grep_pattern, |
| 73 "--format=%H", |
| 74 search_range, |
| 75 ] |
| 76 |
| 77 found_by_hash = _git_execute( |
| 78 git_working_dir, git_args(start_hash), verbose).strip() |
| 79 |
| 80 if verbose: |
| 81 print "2.) Found by hash: " + found_by_hash |
| 82 |
| 83 found_by_commit_pos = _git_execute( |
| 84 git_working_dir, git_args(commit_position), verbose).strip() |
| 85 |
| 86 if verbose: |
| 87 print "3.) Found by commit position: " + found_by_commit_pos |
| 88 |
| 89 # Replace brackets or else they are wrongly interpreted by --grep |
| 90 title = title.replace("[", "\\[") |
| 91 title = title.replace("]", "\\]") |
| 92 |
| 93 found_by_title = _git_execute( |
| 94 git_working_dir, git_args(title), verbose).strip() |
| 95 |
| 96 if verbose: |
| 97 print "4.) Found by title: " + found_by_title |
| 98 |
| 99 hits = ( |
| 100 _convert_to_array(found_by_hash) + |
| 101 _convert_to_array(found_by_commit_pos) + |
| 102 _convert_to_array(found_by_title)) |
| 103 hits = _remove_duplicates(hits) |
| 104 |
| 105 if separator: |
| 106 for current_hit in hits: |
| 107 commits_between = _find_commits_inbetween( |
| 108 separator, current_hit, git_working_dir, verbose) |
| 109 if commits_between != "": |
| 110 return hits |
| 111 return [] |
| 112 |
| 113 return hits |
| 114 |
| 115 def _find_commits_inbetween(start_hash, end_hash, git_working_dir, verbose): |
| 116 commits_between = _git_execute( |
| 117 git_working_dir, |
| 118 ["rev-list", "--reverse", start_hash + ".." + end_hash], |
| 119 verbose) |
| 120 return commits_between.strip() |
| 121 |
| 122 def _convert_to_array(string_of_hashes): |
| 123 return string_of_hashes.splitlines() |
| 124 |
| 125 def _remove_duplicates(array): |
| 126 no_duplicates = [] |
| 127 for current in array: |
| 128 if not current in no_duplicates: |
| 129 no_duplicates.append(current) |
| 130 return no_duplicates |
| 131 |
| 132 def _git_execute(working_dir, args, verbose=False): |
| 133 command = ["git", "-C", working_dir] + args |
| 134 if verbose: |
| 135 print "Git working dir: " + working_dir |
| 136 print "Executing git command:" + str(command) |
| 137 p = Popen(args=command, stdin=PIPE, |
| 138 stdout=PIPE, stderr=PIPE) |
| 139 output, err = p.communicate() |
| 140 rc = p.returncode |
| 141 if rc != 0: |
| 142 raise Exception(err) |
| 143 if verbose: |
| 144 print "Git return value: " + output |
| 145 return output |
| 146 |
| 147 def _pretty_print_entry(hash, git_dir, pre_text, verbose): |
| 148 text_to_print = _git_execute( |
| 149 git_dir, |
| 150 ["show", |
| 151 "--quiet", |
| 152 "--date=iso", |
| 153 hash, |
| 154 "--format=%ad # %H # %s"], |
| 155 verbose) |
| 156 return pre_text + text_to_print.strip() |
| 157 |
| 158 def main(options): |
| 159 all_related_commits = search_all_related_commits( |
| 160 options.git_dir, |
| 161 options.of[0], |
| 162 options.until[0], |
| 163 options.separator, |
| 164 options.verbose) |
| 165 |
| 166 sort_key = lambda x: ( |
| 167 _git_execute( |
| 168 options.git_dir, |
| 169 ["show", "--quiet", "--date=iso", x, "--format=%ad"], |
| 170 options.verbose)).strip() |
| 171 |
| 172 high_level_commits = sorted(all_related_commits.keys(), key=sort_key) |
| 173 |
| 174 for current_key in high_level_commits: |
| 175 if options.prettyprint: |
| 176 yield _pretty_print_entry( |
| 177 current_key, |
| 178 options.git_dir, |
| 179 "+", |
| 180 options.verbose) |
| 181 else: |
| 182 yield "+" + current_key |
| 183 |
| 184 found_commits = all_related_commits[current_key] |
| 185 for current_commit in found_commits: |
| 186 if options.prettyprint: |
| 187 yield _pretty_print_entry( |
| 188 current_commit, |
| 189 options.git_dir, |
| 190 "| ", |
| 191 options.verbose) |
| 192 else: |
| 193 yield "| " + current_commit |
| 194 |
| 195 if __name__ == "__main__": # pragma: no cover |
| 196 parser = argparse.ArgumentParser( |
| 197 "This tool analyzes the commit range between <of> and <until>. " |
| 198 "It finds commits which belong together e.g. Implement/Revert pairs and " |
| 199 "Implement/Port/Revert triples. All supplied hashes need to be " |
| 200 "from the same branch e.g. master.") |
| 201 parser.add_argument("-g", "--git-dir", required=False, default=".", |
| 202 help="The path to your git working directory.") |
| 203 parser.add_argument("--verbose", action="store_true", |
| 204 help="Enables a very verbose output") |
| 205 parser.add_argument("of", nargs=1, |
| 206 help="Hash of the commit to be searched.") |
| 207 parser.add_argument("until", nargs=1, |
| 208 help="Commit when searching should stop") |
| 209 parser.add_argument("--separator", required=False, |
| 210 help="The script will only list related commits " |
| 211 "which are separated by hash <--separator>.") |
| 212 parser.add_argument("--prettyprint", action="store_true", |
| 213 help="Pretty prints the output") |
| 214 |
| 215 args = sys.argv[1:] |
| 216 options = parser.parse_args(args) |
| 217 for current_line in main(options): |
| 218 print current_line |
OLD | NEW |