Index: tools/git/for-all-touched-files.py |
diff --git a/tools/git/for-all-touched-files.py b/tools/git/for-all-touched-files.py |
new file mode 100755 |
index 0000000000000000000000000000000000000000..d776743bec9ac5a773339d06267d39974032d605 |
--- /dev/null |
+++ b/tools/git/for-all-touched-files.py |
@@ -0,0 +1,114 @@ |
+#!/usr/bin/python |
+# Copyright (c) 2011 The Chromium Authors. All rights reserved. |
+# Use of this source code is governed by a BSD-style license that can be |
+# found in the LICENSE file. |
+ |
+""" |
+ Invokes the specified (quoted) command for all files modified |
+ between the current git branch and the specified branch or commit. |
+ |
+ The special token [[FILENAME]] (or whatever you choose using the -t |
+ flag) is replaced with each of the filenames of new or modified files. |
+ |
+ Deleted files are not included. Neither are untracked files. |
+ |
+Synopsis: |
+ %prog [-b BRANCH] [-d] [-x EXTENSIONS|-c] [-t TOKEN] QUOTED_COMMAND |
+ |
+Examples: |
+ %prog -x gyp,gypi "tools/format_xml.py [[FILENAME]]" |
+ %prog -c "tools/sort-headers.py [[FILENAME]]" |
+ %prog -t "~~BINGO~~" "echo I modified ~~BINGO~~" |
+""" |
+ |
+import optparse |
+import os |
+import subprocess |
+import sys |
+ |
+ |
+# List of C++-like source file extensions. |
+_CPP_EXTENSIONS = ('h', 'hh', 'hpp', 'c', 'cc', 'cpp', 'cxx', 'mm',) |
+ |
+ |
+def GitShell(args, ignore_return=False): |
+ """A shell invocation suitable for communicating with git. Returns |
+ output as list of lines, raises exception on error. |
+ """ |
+ job = subprocess.Popen(args, |
+ shell=True, |
+ stdout=subprocess.PIPE, |
+ stderr=subprocess.STDOUT) |
+ (out, err) = job.communicate() |
+ if job.returncode != 0 and not ignore_return: |
+ print out |
+ raise Exception("Error %d running command %s" % ( |
+ job.returncode, args)) |
+ return out.split('\n') |
+ |
+ |
+def FilenamesFromGit(branch_name, extensions): |
+ """Provides a list of all new and modified files listed by [git diff |
+ branch_name] where branch_name can be blank to get a diff of the |
+ workspace. |
+ |
+ Excludes deleted files. |
+ |
+ If extensions is not an empty list, include only files with one of |
+ the extensions on the list. |
+ """ |
+ lines = GitShell('git diff --stat=600,500 %s' % branch_name) |
+ filenames = [] |
+ for line in lines: |
+ line = line.lstrip() |
+ # Avoid summary line, and files that have been deleted (no plus). |
+ if line.find('|') != -1 and line.find('+') != -1: |
+ filename = line.split()[0] |
+ if filename: |
+ filename = filename.rstrip() |
+ ext = filename.rsplit('.')[-1] |
+ if not extensions or ext in extensions: |
+ filenames.append(filename) |
+ return filenames |
+ |
+ |
+def ForAllTouchedFiles(branch_name, extensions, token, command): |
+ """For each new or modified file output by [git diff branch_name], |
+ run command with token replaced with the filename. If extensions is |
+ not empty, do this only for files with one of the extensions in that |
+ list. |
+ """ |
+ filenames = FilenamesFromGit(branch_name, extensions) |
+ for filename in filenames: |
+ os.system(command.replace(token, filename)) |
+ |
+ |
+def main(): |
+ parser = optparse.OptionParser(usage=__doc__) |
+ parser.add_option('-x', '--extensions', default='', dest='extensions', |
+ help='Limits to files with given extensions ' |
+ '(comma-separated).') |
+ parser.add_option('-c', '--cpp', default=False, action='store_true', |
+ dest='cpp_only', |
+ help='Runs your command only on C++-like source files.') |
+ parser.add_option('-t', '--token', default='[[FILENAME]]', dest='token', |
+ help='Sets the token to be replaced for each file ' |
+ 'in your command (default [[FILENAME]]).') |
+ parser.add_option('-b', '--branch', default='origin/master', dest='branch', |
+ help='Sets what to diff to (default origin/master). Set ' |
+ 'to empty to diff workspace against HEAD.') |
+ opts, args = parser.parse_args() |
+ |
+ if not args: |
+ parser.print_help() |
+ sys.exit(1) |
+ |
+ extensions = opts.extensions |
+ if opts.cpp_only: |
+ extensions = _CPP_EXTENSIONS |
+ |
+ ForAllTouchedFiles(opts.branch, extensions, opts.token, args[0]) |
+ |
+ |
+if __name__ == '__main__': |
+ main() |