diff options
author | joi@chromium.org <joi@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-11-01 11:46:52 +0000 |
---|---|---|
committer | joi@chromium.org <joi@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-11-01 11:46:52 +0000 |
commit | 10ab0ed503293ec16bba8b6397e5169c0a3a896b (patch) | |
tree | 92dca0869af16bd34d787b930cf081b8bff4a702 /tools/git | |
parent | f2a4351bb986766e774341b411516306d3e96b2b (diff) | |
download | chromium_src-10ab0ed503293ec16bba8b6397e5169c0a3a896b.zip chromium_src-10ab0ed503293ec16bba8b6397e5169c0a3a896b.tar.gz chromium_src-10ab0ed503293ec16bba8b6397e5169c0a3a896b.tar.bz2 |
Adds a tool that lets you run a command for each modified file on your git branch or in your workspace.
Also, add a flag to sort-headers.py to make it more useful when used in conjunction with that tool, that suppresses the y/N prompt after each diff (as vetting a bulk change may be easier once it's in your working directory, using your favorite diff tool). Refactor the script slightly while I'm in there.
BUG=none
TEST=none
Review URL: http://codereview.chromium.org/8390055
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@108097 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'tools/git')
-rwxr-xr-x | tools/git/for-all-touched-files.py | 114 |
1 files changed, 114 insertions, 0 deletions
diff --git a/tools/git/for-all-touched-files.py b/tools/git/for-all-touched-files.py new file mode 100755 index 0000000..d776743 --- /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() |