Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(744)

Side by Side Diff: tools/git/mffr.py

Issue 1841863002: Update monet. (Closed) Base URL: https://github.com/domokit/monet.git@master
Patch Set: Created 4 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « tools/git/mass-rename.sh ('k') | tools/git/move_source_file.bat » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 #!/usr/bin/env python
2 # Copyright (c) 2013 The Chromium Authors. All rights reserved.
3 # Use of this source code is governed by a BSD-style license that can be
4 # found in the LICENSE file.
5
6 """Usage: mffr.py [-d] [-g *.h] [-g *.cc] REGEXP REPLACEMENT
7
8 This tool performs a fast find-and-replace operation on files in
9 the current git repository.
10
11 The -d flag selects a default set of globs (C++ and Objective-C/C++
12 source files). The -g flag adds a single glob to the list and may
13 be used multiple times. If neither -d nor -g is specified, the tool
14 searches all files (*.*).
15
16 REGEXP uses full Python regexp syntax. REPLACEMENT can use
17 back-references.
18 """
19
20 import optparse
21 import re
22 import subprocess
23 import sys
24
25
26 # We need to use shell=True with subprocess on Windows so that it
27 # finds 'git' from the path, but can lead to undesired behavior on
28 # Linux.
29 _USE_SHELL = (sys.platform == 'win32')
30
31
32 def MultiFileFindReplace(original, replacement, file_globs):
33 """Implements fast multi-file find and replace.
34
35 Given an |original| string and a |replacement| string, find matching
36 files by running git grep on |original| in files matching any
37 pattern in |file_globs|.
38
39 Once files are found, |re.sub| is run to replace |original| with
40 |replacement|. |replacement| may use capture group back-references.
41
42 Args:
43 original: '(#(include|import)\s*["<])chrome/browser/ui/browser.h([>"])'
44 replacement: '\1chrome/browser/ui/browser/browser.h\3'
45 file_globs: ['*.cc', '*.h', '*.m', '*.mm']
46
47 Returns the list of files modified.
48
49 Raises an exception on error.
50 """
51 # Posix extended regular expressions do not reliably support the "\s"
52 # shorthand.
53 posix_ere_original = re.sub(r"\\s", "[[:space:]]", original)
54 if sys.platform == 'win32':
55 posix_ere_original = posix_ere_original.replace('"', '""')
56 out, err = subprocess.Popen(
57 ['git', 'grep', '-E', '--name-only', posix_ere_original,
58 '--'] + file_globs,
59 stdout=subprocess.PIPE,
60 shell=_USE_SHELL).communicate()
61 referees = out.splitlines()
62
63 for referee in referees:
64 with open(referee) as f:
65 original_contents = f.read()
66 contents = re.sub(original, replacement, original_contents)
67 if contents == original_contents:
68 raise Exception('No change in file %s although matched in grep' %
69 referee)
70 with open(referee, 'wb') as f:
71 f.write(contents)
72
73 return referees
74
75
76 def main():
77 parser = optparse.OptionParser(usage='''
78 (1) %prog <options> REGEXP REPLACEMENT
79 REGEXP uses full Python regexp syntax. REPLACEMENT can use back-references.
80
81 (2) %prog <options> -i <file>
82 <file> should contain a list (in Python syntax) of
83 [REGEXP, REPLACEMENT, [GLOBS]] lists, e.g.:
84 [
85 [r"(foo|bar)", r"\1baz", ["*.cc", "*.h"]],
86 ["54", "42"],
87 ]
88 As shown above, [GLOBS] can be omitted for a given search-replace list, in which
89 case the corresponding search-replace will use the globs specified on the
90 command line.''')
91 parser.add_option('-d', action='store_true',
92 dest='use_default_glob',
93 help='Perform the change on C++ and Objective-C(++) source '
94 'and header files.')
95 parser.add_option('-f', action='store_true',
96 dest='force_unsafe_run',
97 help='Perform the run even if there are uncommitted local '
98 'changes.')
99 parser.add_option('-g', action='append',
100 type='string',
101 default=[],
102 metavar="<glob>",
103 dest='user_supplied_globs',
104 help='Perform the change on the specified glob. Can be '
105 'specified multiple times, in which case the globs are '
106 'unioned.')
107 parser.add_option('-i', "--input_file",
108 type='string',
109 action='store',
110 default='',
111 metavar="<file>",
112 dest='input_filename',
113 help='Read arguments from <file> rather than the command '
114 'line. NOTE: To be sure of regular expressions being '
115 'interpreted correctly, use raw strings.')
116 opts, args = parser.parse_args()
117 if opts.use_default_glob and opts.user_supplied_globs:
118 print '"-d" and "-g" cannot be used together'
119 parser.print_help()
120 return 1
121
122 from_file = opts.input_filename != ""
123 if (from_file and len(args) != 0) or (not from_file and len(args) != 2):
124 parser.print_help()
125 return 1
126
127 if not opts.force_unsafe_run:
128 out, err = subprocess.Popen(['git', 'status', '--porcelain'],
129 stdout=subprocess.PIPE,
130 shell=_USE_SHELL).communicate()
131 if out:
132 print 'ERROR: This tool does not print any confirmation prompts,'
133 print 'so you should only run it with a clean staging area and cache'
134 print 'so that reverting a bad find/replace is as easy as running'
135 print ' git checkout -- .'
136 print ''
137 print 'To override this safeguard, pass the -f flag.'
138 return 1
139
140 global_file_globs = ['*.*']
141 if opts.use_default_glob:
142 global_file_globs = ['*.cc', '*.h', '*.m', '*.mm']
143 elif opts.user_supplied_globs:
144 global_file_globs = opts.user_supplied_globs
145
146 # Construct list of search-replace tasks.
147 search_replace_tasks = []
148 if opts.input_filename == '':
149 original = args[0]
150 replacement = args[1]
151 search_replace_tasks.append([original, replacement, global_file_globs])
152 else:
153 f = open(opts.input_filename)
154 search_replace_tasks = eval("".join(f.readlines()))
155 for task in search_replace_tasks:
156 if len(task) == 2:
157 task.append(global_file_globs)
158 f.close()
159
160 for (original, replacement, file_globs) in search_replace_tasks:
161 print 'File globs: %s' % file_globs
162 print 'Original: %s' % original
163 print 'Replacement: %s' % replacement
164 MultiFileFindReplace(original, replacement, file_globs)
165 return 0
166
167
168 if __name__ == '__main__':
169 sys.exit(main())
OLDNEW
« no previous file with comments | « tools/git/mass-rename.sh ('k') | tools/git/move_source_file.bat » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698