diff options
author | joi@chromium.org <joi@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-02-11 15:58:13 +0000 |
---|---|---|
committer | joi@chromium.org <joi@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-02-11 15:58:13 +0000 |
commit | 9646105615bddfeab68456c0c4a937c472867387 (patch) | |
tree | c654d46a26738b04f634800512d70a295e845a0d /tools/git | |
parent | f1a6fd9a547166c76e77320dc3c60d53b2b6de36 (diff) | |
download | chromium_src-9646105615bddfeab68456c0c4a937c472867387.zip chromium_src-9646105615bddfeab68456c0c4a937c472867387.tar.gz chromium_src-9646105615bddfeab68456c0c4a937c472867387.tar.bz2 |
Extract multi-file find and replace into a separate tool.
BUG=none
Review URL: https://codereview.chromium.org/12149005
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@181693 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'tools/git')
-rwxr-xr-x | tools/git/mffr.py | 114 | ||||
-rwxr-xr-x | tools/git/move_source_file.py | 51 |
2 files changed, 119 insertions, 46 deletions
diff --git a/tools/git/mffr.py b/tools/git/mffr.py new file mode 100755 index 0000000..5effc15 --- /dev/null +++ b/tools/git/mffr.py @@ -0,0 +1,114 @@ +#!/usr/bin/env python +# Copyright (c) 2013 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. + +"""Usage: mffr.py [-d] [-g *.h] [-g *.cc] REGEXP REPLACEMENT + +This tool performs a fast find-and-replace operation on files in +the current git repository. + +The -d flag selects a default set of globs (C++ and Objective-C/C++ +source files). The -g flag adds a single glob to the list and may +be used multiple times. If neither -d nor -g is specified, the tool +searches all files (*.*). + +REGEXP uses full Python regexp syntax. REPLACEMENT can use +back-references. +""" + +import re +import subprocess +import sys + + +# We need to use shell=True with subprocess on Windows so that it +# finds 'git' from the path, but can lead to undesired behavior on +# Linux. +_USE_SHELL = (sys.platform == 'win32') + + +def MultiFileFindReplace(original, replacement, file_globs): + """Implements fast multi-file find and replace. + + Given an |original| string and a |replacement| string, find matching + files by running git grep on |original| in files matching any + pattern in |file_globs|. + + Once files are found, |re.sub| is run to replace |original| with + |replacement|. |replacement| may use capture group back-references. + + Args: + original: '(#(include|import)\s*["<])chrome/browser/ui/browser.h([>"])' + replacement: '\1chrome/browser/ui/browser/browser.h\3' + file_globs: ['*.cc', '*.h', '*.m', '*.mm'] + + Returns the list of files modified. + + Raises an exception on error. + """ + # Posix extended regular expressions do not reliably support the "\s" + # shorthand. + posix_ere_original = re.sub(r"\\s", "[[:space:]]", original) + out, err = subprocess.Popen( + ['git', 'grep', '-E', '--name-only', posix_ere_original, + '--'] + file_globs, + stdout=subprocess.PIPE, + shell=_USE_SHELL).communicate() + referees = out.splitlines() + + for referee in referees: + with open(referee) as f: + original_contents = f.read() + contents = re.sub(original, replacement, original_contents) + if contents == original_contents: + raise Exception('No change in file %s although matched in grep' % + referee) + with open(referee, 'wb') as f: + f.write(contents) + + return referees + + +def main(): + file_globs = [] + force_unsafe_run = False + args = sys.argv[1:] + while args and args[0].startswith('-'): + if args[0] == '-d': + file_globs = ['*.cc', '*.h', '*.m', '*.mm'] + args = args[1:] + elif args[0] == '-g': + file_globs.append(args[1]) + args = args[2:] + elif args[0] == '-f': + force_unsafe_run = True + args = args[1:] + if not file_globs: + file_globs = ['*.*'] + if not args: + print globals()['__doc__'] + return 1 + if not force_unsafe_run: + out, err = subprocess.Popen(['git', 'status', '--porcelain'], + stdout=subprocess.PIPE, + shell=_USE_SHELL).communicate() + if out: + print 'ERROR: This tool does not print any confirmation prompts,' + print 'so you should only run it with a clean staging area and cache' + print 'so that reverting a bad find/replace is as easy as running' + print ' git checkout -- .' + print '' + print 'To override this safeguard, pass the -f flag.' + return 1 + original = args[0] + replacement = args[1] + print 'File globs: %s' % file_globs + print 'Original: %s' % original + print 'Replacement: %s' % replacement + MultiFileFindReplace(original, replacement, file_globs) + return 0 + + +if __name__ == '__main__': + sys.exit(main()) diff --git a/tools/git/move_source_file.py b/tools/git/move_source_file.py index 41bf2900..79048af 100755 --- a/tools/git/move_source_file.py +++ b/tools/git/move_source_file.py @@ -22,6 +22,8 @@ import re import subprocess import sys +import mffr + if __name__ == '__main__': # Need to add the directory containing sort-headers.py to the Python # classpath. @@ -57,49 +59,6 @@ def MoveFile(from_path, to_path): raise Exception('Fatal: Failed to run git mv command.') -def MultiFileFindReplace(original, - replacement, - file_globs): - """Implements fast multi-file find and replace. - - Given an |original| string and a |replacement| string, find matching - files by running git grep on |original| in files matching any - pattern in |file_globs|. - - Once files are found, |re.sub| is run to replace |original| with - |replacement|. |replacement| may use capture group back-references. - - Args: - original: '(#(include|import)\s*["<])chrome/browser/ui/browser.h([>"])' - replacement: '\1chrome/browser/ui/browser/browser.h\3' - file_globs: ['*.cc', '*.h', '*.m', '*.mm'] - - Returns the list of files modified. - - Raises an exception on error. - """ - # Posix extended regular expressions do not reliably support the "\s" - # shorthand. - posix_ere_original = re.sub(r"\\s", "[[:space:]]", original) - out, err = subprocess.Popen( - ['git', 'grep', '-E', '--name-only', posix_ere_original, '--'] + - file_globs, - stdout=subprocess.PIPE).communicate() - referees = out.splitlines() - - for referee in referees: - with open(referee) as f: - original_contents = f.read() - contents = re.sub(original, replacement, original_contents) - if contents == original_contents: - raise Exception('No change in file %s although matched in grep' % - referee) - with open(referee, 'w') as f: - f.write(contents) - - return referees - - def UpdatePostMove(from_path, to_path): """Given a file that has moved from |from_path| to |to_path|, updates the moved file's include guard to match the new path and @@ -114,7 +73,7 @@ def UpdatePostMove(from_path, to_path): UpdateIncludeGuard(from_path, to_path) # Update include/import references. - files_with_changed_includes = MultiFileFindReplace( + files_with_changed_includes = mffr.MultiFileFindReplace( r'(#(include|import)\s*["<])%s([>"])' % re.escape(from_path), r'\1%s\3' % to_path, ['*.cc', '*.h', '*.m', '*.mm']) @@ -130,7 +89,7 @@ def UpdatePostMove(from_path, to_path): # This work takes a bit of time. If this script starts feeling too # slow, one good way to speed it up is to make the comment handling # optional under a flag. - MultiFileFindReplace( + mffr.MultiFileFindReplace( r'(//.*)%s' % re.escape(from_path), r'\1%s' % to_path, ['*.cc', '*.h', '*.m', '*.mm']) @@ -143,7 +102,7 @@ def UpdatePostMove(from_path, to_path): return parts[1] else: return parts[0] - MultiFileFindReplace( + mffr.MultiFileFindReplace( r'([\'"])%s([\'"])' % re.escape(PathMinusFirstComponent(from_path)), r'\1%s\2' % PathMinusFirstComponent(to_path), ['*.gyp*']) |