115 lines
3.8 KiB
Python
115 lines
3.8 KiB
Python
|
#!/usr/bin/env 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()
|