1c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot#!/usr/bin/env python 2c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot# 3c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot#===- git-clang-format - ClangFormat Git Integration ---------*- python -*--===# 4c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot# 5c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot# The LLVM Compiler Infrastructure 6c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot# 7c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot# This file is distributed under the University of Illinois Open Source 8c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot# License. See LICENSE.TXT for details. 9c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot# 10c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot#===------------------------------------------------------------------------===# 11c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot 12c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robotr""" 13c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robotclang-format git integration 14c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot============================ 15c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot 16c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team RobotThis file provides a clang-format integration for git. Put it somewhere in your 17c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robotpath and ensure that it is executable. Then, "git clang-format" will invoke 18c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robotclang-format on the changes in current files or a specific commit. 19c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot 20c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team RobotFor further details, run: 21c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robotgit clang-format -h 22c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot 23c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team RobotRequires Python 2.7 24c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot""" 25c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot 26c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robotimport argparse 27c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robotimport collections 28c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robotimport contextlib 29c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robotimport errno 30c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robotimport os 31c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robotimport re 32c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robotimport subprocess 33c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robotimport sys 34c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot 35c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robotusage = 'git clang-format [OPTIONS] [<commit>] [<commit>] [--] [<file>...]' 36c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot 37c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robotdesc = ''' 38c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team RobotIf zero or one commits are given, run clang-format on all lines that differ 39c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robotbetween the working directory and <commit>, which defaults to HEAD. Changes are 40c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robotonly applied to the working directory. 41c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot 42c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team RobotIf two commits are given (requires --diff), run clang-format on all lines in the 43c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robotsecond <commit> that differ from the first <commit>. 44c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot 45c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team RobotThe following git-config settings set the default of the corresponding option: 46c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot clangFormat.binary 47c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot clangFormat.commit 48c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot clangFormat.extension 49c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot clangFormat.style 50c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot''' 51c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot 52c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot# Name of the temporary index file in which save the output of clang-format. 53c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot# This file is created within the .git directory. 54c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robottemp_index_basename = 'clang-format-index' 55c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot 56c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot 57c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team RobotRange = collections.namedtuple('Range', 'start, count') 58c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot 59c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot 60c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robotdef main(): 61c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot config = load_git_config() 62c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot 63c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot # In order to keep '--' yet allow options after positionals, we need to 64c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot # check for '--' ourselves. (Setting nargs='*' throws away the '--', while 65c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot # nargs=argparse.REMAINDER disallows options after positionals.) 66c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot argv = sys.argv[1:] 67c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot try: 68c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot idx = argv.index('--') 69c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot except ValueError: 70c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot dash_dash = [] 71c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot else: 72c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot dash_dash = argv[idx:] 73c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot argv = argv[:idx] 74c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot 75c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot default_extensions = ','.join([ 76c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot # From clang/lib/Frontend/FrontendOptions.cpp, all lower case 77c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot 'c', 'h', # C 78c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot 'm', # ObjC 79c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot 'mm', # ObjC++ 80c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot 'cc', 'cp', 'cpp', 'c++', 'cxx', 'hpp', # C++ 81c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot # Other languages that clang-format supports 82c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot 'proto', 'protodevel', # Protocol Buffers 83c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot 'java', # Java 84c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot 'js', # JavaScript 85c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot 'ts', # TypeScript 86c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot ]) 87c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot 88c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot p = argparse.ArgumentParser( 89c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot usage=usage, formatter_class=argparse.RawDescriptionHelpFormatter, 90c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot description=desc) 91c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot p.add_argument('--binary', 92c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot default=config.get('clangformat.binary', 'clang-format'), 93c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot help='path to clang-format'), 94c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot p.add_argument('--commit', 95c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot default=config.get('clangformat.commit', 'HEAD'), 96c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot help='default commit to use if none is specified'), 97c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot p.add_argument('--diff', action='store_true', 98c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot help='print a diff instead of applying the changes') 99c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot p.add_argument('--extensions', 100c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot default=config.get('clangformat.extensions', 101c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot default_extensions), 102c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot help=('comma-separated list of file extensions to format, ' 103c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot 'excluding the period and case-insensitive')), 104c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot p.add_argument('-f', '--force', action='store_true', 105c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot help='allow changes to unstaged files') 106c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot p.add_argument('-p', '--patch', action='store_true', 107c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot help='select hunks interactively') 108c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot p.add_argument('-q', '--quiet', action='count', default=0, 109c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot help='print less information') 110c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot p.add_argument('--style', 111c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot default=config.get('clangformat.style', None), 112c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot help='passed to clang-format'), 113c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot p.add_argument('-v', '--verbose', action='count', default=0, 114c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot help='print extra information') 115c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot # We gather all the remaining positional arguments into 'args' since we need 116c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot # to use some heuristics to determine whether or not <commit> was present. 117c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot # However, to print pretty messages, we make use of metavar and help. 118c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot p.add_argument('args', nargs='*', metavar='<commit>', 119c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot help='revision from which to compute the diff') 120c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot p.add_argument('ignored', nargs='*', metavar='<file>...', 121c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot help='if specified, only consider differences in these files') 122c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot opts = p.parse_args(argv) 123c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot 124c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot opts.verbose -= opts.quiet 125c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot del opts.quiet 126c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot 127c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot commits, files = interpret_args(opts.args, dash_dash, opts.commit) 128c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot if len(commits) > 1: 129c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot if not opts.diff: 130c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot die('--diff is required when two commits are given') 131c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot else: 132c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot if len(commits) > 2: 133c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot die('at most two commits allowed; %d given' % len(commits)) 134c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot changed_lines = compute_diff_and_extract_lines(commits, files) 135c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot if opts.verbose >= 1: 136c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot ignored_files = set(changed_lines) 137c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot filter_by_extension(changed_lines, opts.extensions.lower().split(',')) 138c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot if opts.verbose >= 1: 139c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot ignored_files.difference_update(changed_lines) 140c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot if ignored_files: 141c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot print 'Ignoring changes in the following files (wrong extension):' 142c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot for filename in ignored_files: 143c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot print ' ', filename 144c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot if changed_lines: 145c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot print 'Running clang-format on the following files:' 146c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot for filename in changed_lines: 147c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot print ' ', filename 148c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot if not changed_lines: 149c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot print 'no modified files to format' 150c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot return 151c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot # The computed diff outputs absolute paths, so we must cd before accessing 152c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot # those files. 153c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot cd_to_toplevel() 154c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot if len(commits) > 1: 155c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot old_tree = commits[1] 156c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot new_tree = run_clang_format_and_save_to_tree(changed_lines, 157c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot revision=commits[1], 158c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot binary=opts.binary, 159c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot style=opts.style) 160c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot else: 161c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot old_tree = create_tree_from_workdir(changed_lines) 162c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot new_tree = run_clang_format_and_save_to_tree(changed_lines, 163c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot binary=opts.binary, 164c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot style=opts.style) 165c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot if opts.verbose >= 1: 166c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot print 'old tree:', old_tree 167c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot print 'new tree:', new_tree 168c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot if old_tree == new_tree: 169c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot if opts.verbose >= 0: 170c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot print 'clang-format did not modify any files' 171c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot elif opts.diff: 172c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot print_diff(old_tree, new_tree) 173c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot else: 174c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot changed_files = apply_changes(old_tree, new_tree, force=opts.force, 175c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot patch_mode=opts.patch) 176c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot if (opts.verbose >= 0 and not opts.patch) or opts.verbose >= 1: 177c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot print 'changed files:' 178c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot for filename in changed_files: 179c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot print ' ', filename 180c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot 181c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot 182c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robotdef load_git_config(non_string_options=None): 183c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot """Return the git configuration as a dictionary. 184c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot 185c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot All options are assumed to be strings unless in `non_string_options`, in which 186c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot is a dictionary mapping option name (in lower case) to either "--bool" or 187c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot "--int".""" 188c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot if non_string_options is None: 189c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot non_string_options = {} 190c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot out = {} 191c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot for entry in run('git', 'config', '--list', '--null').split('\0'): 192c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot if entry: 193c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot name, value = entry.split('\n', 1) 194c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot if name in non_string_options: 195c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot value = run('git', 'config', non_string_options[name], name) 196c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot out[name] = value 197c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot return out 198c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot 199c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot 200c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robotdef interpret_args(args, dash_dash, default_commit): 201c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot """Interpret `args` as "[commits] [--] [files]" and return (commits, files). 202c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot 203c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot It is assumed that "--" and everything that follows has been removed from 204c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot args and placed in `dash_dash`. 205c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot 206c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot If "--" is present (i.e., `dash_dash` is non-empty), the arguments to its 207c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot left (if present) are taken as commits. Otherwise, the arguments are checked 208c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot from left to right if they are commits or files. If commits are not given, 209c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot a list with `default_commit` is used.""" 210c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot if dash_dash: 211c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot if len(args) == 0: 212c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot commits = [default_commit] 213c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot else: 214c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot commits = args 215c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot for commit in commits: 216c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot object_type = get_object_type(commit) 217c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot if object_type not in ('commit', 'tag'): 218c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot if object_type is None: 219c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot die("'%s' is not a commit" % commit) 220c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot else: 221c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot die("'%s' is a %s, but a commit was expected" % (commit, object_type)) 222c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot files = dash_dash[1:] 223c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot elif args: 224c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot commits = [] 225c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot while args: 226c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot if not disambiguate_revision(args[0]): 227c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot break 228c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot commits.append(args.pop(0)) 229c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot if not commits: 230c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot commits = [default_commit] 231c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot files = args 232c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot else: 233c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot commits = [default_commit] 234c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot files = [] 235c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot return commits, files 236c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot 237c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot 238c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robotdef disambiguate_revision(value): 239c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot """Returns True if `value` is a revision, False if it is a file, or dies.""" 240c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot # If `value` is ambiguous (neither a commit nor a file), the following 241c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot # command will die with an appropriate error message. 242c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot run('git', 'rev-parse', value, verbose=False) 243c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot object_type = get_object_type(value) 244c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot if object_type is None: 245c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot return False 246c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot if object_type in ('commit', 'tag'): 247c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot return True 248c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot die('`%s` is a %s, but a commit or filename was expected' % 249c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot (value, object_type)) 250c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot 251c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot 252c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robotdef get_object_type(value): 253c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot """Returns a string description of an object's type, or None if it is not 254c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot a valid git object.""" 255c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot cmd = ['git', 'cat-file', '-t', value] 256c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) 257c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot stdout, stderr = p.communicate() 258c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot if p.returncode != 0: 259c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot return None 260c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot return stdout.strip() 261c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot 262c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot 263c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robotdef compute_diff_and_extract_lines(commits, files): 264c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot """Calls compute_diff() followed by extract_lines().""" 265c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot diff_process = compute_diff(commits, files) 266c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot changed_lines = extract_lines(diff_process.stdout) 267c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot diff_process.stdout.close() 268c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot diff_process.wait() 269c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot if diff_process.returncode != 0: 270c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot # Assume error was already printed to stderr. 271c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot sys.exit(2) 272c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot return changed_lines 273c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot 274c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot 275c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robotdef compute_diff(commits, files): 276c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot """Return a subprocess object producing the diff from `commits`. 277c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot 278c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot The return value's `stdin` file object will produce a patch with the 279c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot differences between the working directory and the first commit if a single 280c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot one was specified, or the difference between both specified commits, filtered 281c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot on `files` (if non-empty). Zero context lines are used in the patch.""" 282c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot git_tool = 'diff-index' 283c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot if len(commits) > 1: 284c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot git_tool = 'diff-tree' 285c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot cmd = ['git', git_tool, '-p', '-U0'] + commits + ['--'] 286c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot cmd.extend(files) 287c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot p = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE) 288c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot p.stdin.close() 289c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot return p 290c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot 291c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot 292c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robotdef extract_lines(patch_file): 293c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot """Extract the changed lines in `patch_file`. 294c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot 295c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot The return value is a dictionary mapping filename to a list of (start_line, 296c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot line_count) pairs. 297c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot 298c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot The input must have been produced with ``-U0``, meaning unidiff format with 299c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot zero lines of context. The return value is a dict mapping filename to a 300c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot list of line `Range`s.""" 301c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot matches = {} 302c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot for line in patch_file: 303c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot match = re.search(r'^\+\+\+\ [^/]+/(.*)', line) 304c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot if match: 305c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot filename = match.group(1).rstrip('\r\n') 306c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot match = re.search(r'^@@ -[0-9,]+ \+(\d+)(,(\d+))?', line) 307c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot if match: 308c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot start_line = int(match.group(1)) 309c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot line_count = 1 310c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot if match.group(3): 311c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot line_count = int(match.group(3)) 312c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot if line_count > 0: 313c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot matches.setdefault(filename, []).append(Range(start_line, line_count)) 314c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot return matches 315c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot 316c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot 317c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robotdef filter_by_extension(dictionary, allowed_extensions): 318c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot """Delete every key in `dictionary` that doesn't have an allowed extension. 319c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot 320c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot `allowed_extensions` must be a collection of lowercase file extensions, 321c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot excluding the period.""" 322c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot allowed_extensions = frozenset(allowed_extensions) 323c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot for filename in dictionary.keys(): 324c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot base_ext = filename.rsplit('.', 1) 325c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot if len(base_ext) == 1 or base_ext[1].lower() not in allowed_extensions: 326c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot del dictionary[filename] 327c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot 328c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot 329c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robotdef cd_to_toplevel(): 330c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot """Change to the top level of the git repository.""" 331c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot toplevel = run('git', 'rev-parse', '--show-toplevel') 332c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot os.chdir(toplevel) 333c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot 334c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot 335c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robotdef create_tree_from_workdir(filenames): 336c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot """Create a new git tree with the given files from the working directory. 337c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot 338c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot Returns the object ID (SHA-1) of the created tree.""" 339c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot return create_tree(filenames, '--stdin') 340c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot 341c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot 342c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robotdef run_clang_format_and_save_to_tree(changed_lines, revision=None, 343c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot binary='clang-format', style=None): 344c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot """Run clang-format on each file and save the result to a git tree. 345c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot 346c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot Returns the object ID (SHA-1) of the created tree.""" 347c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot def index_info_generator(): 348c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot for filename, line_ranges in changed_lines.iteritems(): 349c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot if revision: 350c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot git_metadata_cmd = ['git', 'ls-tree', 351c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot '%s:%s' % (revision, os.path.dirname(filename)), 352c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot os.path.basename(filename)] 353c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot git_metadata = subprocess.Popen(git_metadata_cmd, stdin=subprocess.PIPE, 354c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot stdout=subprocess.PIPE) 355c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot stdout = git_metadata.communicate()[0] 356c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot mode = oct(int(stdout.split()[0], 8)) 357c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot else: 358c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot mode = oct(os.stat(filename).st_mode) 359c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot blob_id = clang_format_to_blob(filename, line_ranges, 360c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot revision=revision, 361c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot binary=binary, 362c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot style=style) 363c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot yield '%s %s\t%s' % (mode, blob_id, filename) 364c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot return create_tree(index_info_generator(), '--index-info') 365c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot 366c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot 367c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robotdef create_tree(input_lines, mode): 368c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot """Create a tree object from the given input. 369c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot 370c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot If mode is '--stdin', it must be a list of filenames. If mode is 371c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot '--index-info' is must be a list of values suitable for "git update-index 372c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot --index-info", such as "<mode> <SP> <sha1> <TAB> <filename>". Any other mode 373c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot is invalid.""" 374c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot assert mode in ('--stdin', '--index-info') 375c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot cmd = ['git', 'update-index', '--add', '-z', mode] 376c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot with temporary_index_file(): 377c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot p = subprocess.Popen(cmd, stdin=subprocess.PIPE) 378c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot for line in input_lines: 379c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot p.stdin.write('%s\0' % line) 380c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot p.stdin.close() 381c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot if p.wait() != 0: 382c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot die('`%s` failed' % ' '.join(cmd)) 383c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot tree_id = run('git', 'write-tree') 384c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot return tree_id 385c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot 386c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot 387c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robotdef clang_format_to_blob(filename, line_ranges, revision=None, 388c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot binary='clang-format', style=None): 389c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot """Run clang-format on the given file and save the result to a git blob. 390c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot 391c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot Runs on the file in `revision` if not None, or on the file in the working 392c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot directory if `revision` is None. 393c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot 394c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot Returns the object ID (SHA-1) of the created blob.""" 395c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot clang_format_cmd = [binary] 396c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot if style: 397c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot clang_format_cmd.extend(['-style='+style]) 398c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot clang_format_cmd.extend([ 399c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot '-lines=%s:%s' % (start_line, start_line+line_count-1) 400c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot for start_line, line_count in line_ranges]) 401c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot if revision: 402c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot clang_format_cmd.extend(['-assume-filename='+filename]) 403c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot git_show_cmd = ['git', 'cat-file', 'blob', '%s:%s' % (revision, filename)] 404c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot git_show = subprocess.Popen(git_show_cmd, stdin=subprocess.PIPE, 405c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot stdout=subprocess.PIPE) 406c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot git_show.stdin.close() 407c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot clang_format_stdin = git_show.stdout 408c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot else: 409c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot clang_format_cmd.extend([filename]) 410c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot git_show = None 411c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot clang_format_stdin = subprocess.PIPE 412c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot try: 413c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot clang_format = subprocess.Popen(clang_format_cmd, stdin=clang_format_stdin, 414c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot stdout=subprocess.PIPE) 415c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot if clang_format_stdin == subprocess.PIPE: 416c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot clang_format_stdin = clang_format.stdin 417c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot except OSError as e: 418c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot if e.errno == errno.ENOENT: 419c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot die('cannot find executable "%s"' % binary) 420c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot else: 421c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot raise 422c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot clang_format_stdin.close() 423c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot hash_object_cmd = ['git', 'hash-object', '-w', '--path='+filename, '--stdin'] 424c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot hash_object = subprocess.Popen(hash_object_cmd, stdin=clang_format.stdout, 425c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot stdout=subprocess.PIPE) 426c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot clang_format.stdout.close() 427c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot stdout = hash_object.communicate()[0] 428c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot if hash_object.returncode != 0: 429c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot die('`%s` failed' % ' '.join(hash_object_cmd)) 430c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot if clang_format.wait() != 0: 431c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot die('`%s` failed' % ' '.join(clang_format_cmd)) 432c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot if git_show and git_show.wait() != 0: 433c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot die('`%s` failed' % ' '.join(git_show_cmd)) 434c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot return stdout.rstrip('\r\n') 435c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot 436c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot 437c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot@contextlib.contextmanager 438c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robotdef temporary_index_file(tree=None): 439c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot """Context manager for setting GIT_INDEX_FILE to a temporary file and deleting 440c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot the file afterward.""" 441c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot index_path = create_temporary_index(tree) 442c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot old_index_path = os.environ.get('GIT_INDEX_FILE') 443c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot os.environ['GIT_INDEX_FILE'] = index_path 444c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot try: 445c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot yield 446c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot finally: 447c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot if old_index_path is None: 448c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot del os.environ['GIT_INDEX_FILE'] 449c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot else: 450c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot os.environ['GIT_INDEX_FILE'] = old_index_path 451c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot os.remove(index_path) 452c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot 453c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot 454c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robotdef create_temporary_index(tree=None): 455c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot """Create a temporary index file and return the created file's path. 456c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot 457c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot If `tree` is not None, use that as the tree to read in. Otherwise, an 458c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot empty index is created.""" 459c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot gitdir = run('git', 'rev-parse', '--git-dir') 460c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot path = os.path.join(gitdir, temp_index_basename) 461c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot if tree is None: 462c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot tree = '--empty' 463c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot run('git', 'read-tree', '--index-output='+path, tree) 464c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot return path 465c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot 466c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot 467c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robotdef print_diff(old_tree, new_tree): 468c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot """Print the diff between the two trees to stdout.""" 469c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot # We use the porcelain 'diff' and not plumbing 'diff-tree' because the output 470c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot # is expected to be viewed by the user, and only the former does nice things 471c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot # like color and pagination. 472c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot # 473c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot # We also only print modified files since `new_tree` only contains the files 474c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot # that were modified, so unmodified files would show as deleted without the 475c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot # filter. 476c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot subprocess.check_call(['git', 'diff', '--diff-filter=M', old_tree, new_tree, 477c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot '--']) 478c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot 479c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot 480c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robotdef apply_changes(old_tree, new_tree, force=False, patch_mode=False): 481c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot """Apply the changes in `new_tree` to the working directory. 482c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot 483c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot Bails if there are local changes in those files and not `force`. If 484c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot `patch_mode`, runs `git checkout --patch` to select hunks interactively.""" 485c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot changed_files = run('git', 'diff-tree', '--diff-filter=M', '-r', '-z', 486c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot '--name-only', old_tree, 487c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot new_tree).rstrip('\0').split('\0') 488c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot if not force: 489c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot unstaged_files = run('git', 'diff-files', '--name-status', *changed_files) 490c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot if unstaged_files: 491c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot print >>sys.stderr, ('The following files would be modified but ' 492c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot 'have unstaged changes:') 493c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot print >>sys.stderr, unstaged_files 494c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot print >>sys.stderr, 'Please commit, stage, or stash them first.' 495c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot sys.exit(2) 496c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot if patch_mode: 497c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot # In patch mode, we could just as well create an index from the new tree 498c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot # and checkout from that, but then the user will be presented with a 499c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot # message saying "Discard ... from worktree". Instead, we use the old 500c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot # tree as the index and checkout from new_tree, which gives the slightly 501c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot # better message, "Apply ... to index and worktree". This is not quite 502c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot # right, since it won't be applied to the user's index, but oh well. 503c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot with temporary_index_file(old_tree): 504c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot subprocess.check_call(['git', 'checkout', '--patch', new_tree]) 505c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot index_tree = old_tree 506c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot else: 507c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot with temporary_index_file(new_tree): 508c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot run('git', 'checkout-index', '-a', '-f') 509c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot return changed_files 510c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot 511c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot 512c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robotdef run(*args, **kwargs): 513c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot stdin = kwargs.pop('stdin', '') 514c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot verbose = kwargs.pop('verbose', True) 515c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot strip = kwargs.pop('strip', True) 516c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot for name in kwargs: 517c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot raise TypeError("run() got an unexpected keyword argument '%s'" % name) 518c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot p = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE, 519c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot stdin=subprocess.PIPE) 520c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot stdout, stderr = p.communicate(input=stdin) 521c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot if p.returncode == 0: 522c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot if stderr: 523c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot if verbose: 524c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot print >>sys.stderr, '`%s` printed to stderr:' % ' '.join(args) 525c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot print >>sys.stderr, stderr.rstrip() 526c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot if strip: 527c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot stdout = stdout.rstrip('\r\n') 528c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot return stdout 529c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot if verbose: 530c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot print >>sys.stderr, '`%s` returned %s' % (' '.join(args), p.returncode) 531c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot if stderr: 532c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot print >>sys.stderr, stderr.rstrip() 533c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot sys.exit(2) 534c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot 535c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot 536c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robotdef die(message): 537c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot print >>sys.stderr, 'error:', message 538c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot sys.exit(2) 539c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot 540c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot 541c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robotif __name__ == '__main__': 542c9cc9e7d29b8970d8ddb734c88fb62d01e0b727android-build-team Robot main() 543