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

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

Issue 17527004: Augment mffr.py to be able to take arguments from a file. (Closed) Base URL: http://git.chromium.org/chromium/src.git@master
Patch Set: Created 7 years, 6 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 | « no previous file | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 #!/usr/bin/env python 1 #!/usr/bin/env python
2 # Copyright (c) 2013 The Chromium Authors. All rights reserved. 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 3 # Use of this source code is governed by a BSD-style license that can be
4 # found in the LICENSE file. 4 # found in the LICENSE file.
5 5
6 """Usage: mffr.py [-d] [-g *.h] [-g *.cc] REGEXP REPLACEMENT 6 """Usage: mffr.py [-d] [-g *.h] [-g *.cc] REGEXP REPLACEMENT
7 7
8 This tool performs a fast find-and-replace operation on files in 8 This tool performs a fast find-and-replace operation on files in
9 the current git repository. 9 the current git repository.
10 10
11 The -d flag selects a default set of globs (C++ and Objective-C/C++ 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 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 13 be used multiple times. If neither -d nor -g is specified, the tool
14 searches all files (*.*). 14 searches all files (*.*).
15 15
16 REGEXP uses full Python regexp syntax. REPLACEMENT can use 16 REGEXP uses full Python regexp syntax. REPLACEMENT can use
17 back-references. 17 back-references.
18 """ 18 """
19 19
20 import optparse
20 import re 21 import re
21 import subprocess 22 import subprocess
22 import sys 23 import sys
23 24
24 25
25 # We need to use shell=True with subprocess on Windows so that it 26 # We need to use shell=True with subprocess on Windows so that it
26 # finds 'git' from the path, but can lead to undesired behavior on 27 # finds 'git' from the path, but can lead to undesired behavior on
27 # Linux. 28 # Linux.
28 _USE_SHELL = (sys.platform == 'win32') 29 _USE_SHELL = (sys.platform == 'win32')
29 30
(...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after
66 if contents == original_contents: 67 if contents == original_contents:
67 raise Exception('No change in file %s although matched in grep' % 68 raise Exception('No change in file %s although matched in grep' %
68 referee) 69 referee)
69 with open(referee, 'wb') as f: 70 with open(referee, 'wb') as f:
70 f.write(contents) 71 f.write(contents)
71 72
72 return referees 73 return referees
73 74
74 75
75 def main(): 76 def main():
76 file_globs = [] 77 parser = optparse.OptionParser(usage='''
77 force_unsafe_run = False 78 (1) %prog <options> REGEXP REPLACEMENT
78 args = sys.argv[1:] 79 REGEXP uses full Python regexp syntax. REPLACEMENT can use back-references.
79 while args and args[0].startswith('-'): 80
80 if args[0] == '-d': 81 (2) %prog <options> -i <file>
81 file_globs = ['*.cc', '*.h', '*.m', '*.mm'] 82 <file> should contain a list of [REGEXP, REPLACEMENT, [GLOBS]] lists, e.g.:
Jói 2013/06/21 11:29:35 Perhaps make it more explicit, "...a list (in Pyth
blundell 2013/06/21 13:33:07 Done.
82 args = args[1:] 83 [
83 elif args[0] == '-g': 84 [r"(foo|bar)", r"\1baz", ["*.cc", "*.h"]],
84 file_globs.append(args[1]) 85 ["54", "42"],
85 args = args[2:] 86 ]
86 elif args[0] == '-f': 87 As shown above, [GLOBS] can be omitted for a given search-replace list, in which
87 force_unsafe_run = True 88 case the corresponding search-replace will use the globs specified on the
88 args = args[1:] 89 command line.''')
89 if not file_globs: 90 parser.add_option('-d', action='store_true',
90 file_globs = ['*.*'] 91 dest='use_default_glob',
91 if not args: 92 help='Perform the change on C++ and Objective-C(++) source '
92 print globals()['__doc__'] 93 'and header files.')
94 parser.add_option('-f', action='store_true',
95 dest='force_unsafe_run',
96 help='Perform the run even if there are uncommitted local '
97 'changes.')
98 parser.add_option('-g', action='append',
99 type='string',
100 default=[],
101 metavar="<glob>",
102 dest='user_supplied_globs',
103 help='Perform the change on the specified glob. Can be '
104 'specified multiple times, in which case the globs are '
105 'unioned.')
106 parser.add_option('-i', "--input_file",
107 type='string',
108 action='store',
109 default='',
110 metavar="<file>",
111 dest='input_filename',
112 help='Read arguments from <file> rather than the command '
113 'line. NOTE: To be sure of regular expressions being '
114 'interpreted correctly, use raw strings.')
115 opts, args = parser.parse_args()
116 if opts.use_default_glob and opts.user_supplied_globs:
117 print '"-d" and "-g" cannot be used together'
118 parser.print_help()
93 return 1 119 return 1
94 if not force_unsafe_run: 120
121 from_file = opts.input_filename != ""
122 if (from_file and len(args) != 0) or (not from_file and len(args) != 2):
123 parser.print_help()
124 return 1
125
126 if not opts.force_unsafe_run:
95 out, err = subprocess.Popen(['git', 'status', '--porcelain'], 127 out, err = subprocess.Popen(['git', 'status', '--porcelain'],
96 stdout=subprocess.PIPE, 128 stdout=subprocess.PIPE,
97 shell=_USE_SHELL).communicate() 129 shell=_USE_SHELL).communicate()
98 if out: 130 if out:
99 print 'ERROR: This tool does not print any confirmation prompts,' 131 print 'ERROR: This tool does not print any confirmation prompts,'
100 print 'so you should only run it with a clean staging area and cache' 132 print 'so you should only run it with a clean staging area and cache'
101 print 'so that reverting a bad find/replace is as easy as running' 133 print 'so that reverting a bad find/replace is as easy as running'
102 print ' git checkout -- .' 134 print ' git checkout -- .'
103 print '' 135 print ''
104 print 'To override this safeguard, pass the -f flag.' 136 print 'To override this safeguard, pass the -f flag.'
105 return 1 137 return 1
106 original = args[0] 138
107 replacement = args[1] 139 global_file_globs = ['*.*']
108 print 'File globs: %s' % file_globs 140 if opts.use_default_glob:
109 print 'Original: %s' % original 141 global_file_globs = ['*.cc', '*.h', '*.m', '*.mm']
110 print 'Replacement: %s' % replacement 142 elif opts.user_supplied_globs:
111 MultiFileFindReplace(original, replacement, file_globs) 143 global_file_globs = opts.user_supplied_globs
144
145 # Construct list of search-replace tasks.
146 search_replace_tasks = []
147 if opts.input_filename == '':
148 original = args[0]
149 replacement = args[1]
150 search_replace_tasks.append([original, replacement, global_file_globs])
151 else:
152 f = open(opts.input_filename)
153 search_replace_tasks = eval("".join(f.readlines()))
154 for task in search_replace_tasks:
155 if len(task) == 2:
156 task.append(global_file_globs)
157 f.close()
158
159 for (original, replacement, file_globs) in search_replace_tasks:
160 print 'File globs: %s' % file_globs
161 print 'Original: %s' % original
162 print 'Replacement: %s' % replacement
163 MultiFileFindReplace(original, replacement, file_globs)
112 return 0 164 return 0
113 165
114 166
115 if __name__ == '__main__': 167 if __name__ == '__main__':
116 sys.exit(main()) 168 sys.exit(main())
OLDNEW
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698