summaryrefslogtreecommitdiffstats
path: root/tools/git
diff options
context:
space:
mode:
authorjoi@chromium.org <joi@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-02-11 15:58:13 +0000
committerjoi@chromium.org <joi@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-02-11 15:58:13 +0000
commit9646105615bddfeab68456c0c4a937c472867387 (patch)
treec654d46a26738b04f634800512d70a295e845a0d /tools/git
parentf1a6fd9a547166c76e77320dc3c60d53b2b6de36 (diff)
downloadchromium_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-xtools/git/mffr.py114
-rwxr-xr-xtools/git/move_source_file.py51
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*'])