forked from chromium/chromium
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
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
- Loading branch information
joi@chromium.org
committed
Feb 11, 2013
1 parent
f1a6fd9
commit 9646105
Showing
2 changed files
with
119 additions
and
46 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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()) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters