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

Side by Side Diff: tools/svndiff.py

Issue 19512002: svndiff.py: add support for git checkouts (Closed) Base URL: http://skia.googlecode.com/svn/trunk/
Patch Set: Created 7 years, 5 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 | Annotate | Revision Log
« 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/python 1 #!/usr/bin/python
epoger 2013/07/17 04:38:25 Tested with JSON expectations as follows: # Rever
2 ''' 2 '''
3 Copyright 2012 Google Inc. 3 Copyright 2012 Google Inc.
4 4
5 Use of this source code is governed by a BSD-style license that can be 5 Use of this source code is governed by a BSD-style license that can be
6 found in the LICENSE file. 6 found in the LICENSE file.
7 ''' 7 '''
8 8
9 ''' 9 '''
10 Generates a visual diff of all pending changes in the local SVN checkout. 10 Generates a visual diff of all pending changes in the local SVN (or git!)
11 checkout.
11 12
12 Launch with --help to see more information. 13 Launch with --help to see more information.
13 14
15 TODO(epoger): Now that this tool supports either git or svn, rename it.
14 TODO(epoger): Fix indentation in this file (2-space indents, not 4-space). 16 TODO(epoger): Fix indentation in this file (2-space indents, not 4-space).
15 ''' 17 '''
16 18
17 # common Python modules 19 # common Python modules
18 import optparse 20 import optparse
19 import os 21 import os
20 import re 22 import re
21 import shutil 23 import shutil
24 import subprocess
22 import sys 25 import sys
23 import tempfile 26 import tempfile
24 import urllib2 27 import urllib2
25 28
26 # Imports from within Skia 29 # Imports from within Skia
27 # 30 #
28 # We need to add the 'gm' directory, so that we can import gm_json.py within 31 # We need to add the 'gm' directory, so that we can import gm_json.py within
29 # that directory. That script allows us to parse the actual-results.json file 32 # that directory. That script allows us to parse the actual-results.json file
30 # written out by the GM tool. 33 # written out by the GM tool.
31 # Make sure that the 'gm' dir is in the PYTHONPATH, but add it at the *end* 34 # Make sure that the 'gm' dir is in the PYTHONPATH, but add it at the *end*
32 # so any dirs that are already in the PYTHONPATH will be preferred. 35 # so any dirs that are already in the PYTHONPATH will be preferred.
33 # 36 #
34 # This assumes that the 'gm' directory has been checked out as a sibling of 37 # This assumes that the 'gm' directory has been checked out as a sibling of
35 # the 'tools' directory containing this script, which will be the case if 38 # the 'tools' directory containing this script, which will be the case if
36 # 'trunk' was checked out as a single unit. 39 # 'trunk' was checked out as a single unit.
37 GM_DIRECTORY = os.path.realpath( 40 GM_DIRECTORY = os.path.realpath(
38 os.path.join(os.path.dirname(os.path.dirname(__file__)), 'gm')) 41 os.path.join(os.path.dirname(os.path.dirname(__file__)), 'gm'))
39 if GM_DIRECTORY not in sys.path: 42 if GM_DIRECTORY not in sys.path:
40 sys.path.append(GM_DIRECTORY) 43 sys.path.append(GM_DIRECTORY)
41 import gm_json 44 import gm_json
42 import jsondiff 45 import jsondiff
43 import svn 46 import svn
44 47
45 USAGE_STRING = 'Usage: %s [options]' 48 USAGE_STRING = 'Usage: %s [options]'
46 HELP_STRING = ''' 49 HELP_STRING = '''
47 50
48 Generates a visual diff of all pending changes in the local SVN checkout. 51 Generates a visual diff of all pending changes in the local SVN/git checkout.
49 52
50 This includes a list of all files that have been added, deleted, or modified 53 This includes a list of all files that have been added, deleted, or modified
51 (as far as SVN knows about). For any image modifications, pixel diffs will 54 (as far as SVN/git knows about). For any image modifications, pixel diffs will
52 be generated. 55 be generated.
53 56
54 ''' 57 '''
55 58
56 IMAGE_FILENAME_RE = re.compile(gm_json.IMAGE_FILENAME_PATTERN) 59 IMAGE_FILENAME_RE = re.compile(gm_json.IMAGE_FILENAME_PATTERN)
57 60
58 TRUNK_PATH = os.path.join(os.path.dirname(__file__), os.pardir) 61 TRUNK_PATH = os.path.join(os.path.dirname(__file__), os.pardir)
59 62
60 OPTION_DEST_DIR = '--dest-dir' 63 OPTION_DEST_DIR = '--dest-dir'
61 OPTION_PATH_TO_SKDIFF = '--path-to-skdiff' 64 OPTION_PATH_TO_SKDIFF = '--path-to-skdiff'
(...skipping 91 matching lines...) Expand 10 before | Expand all | Expand 10 after
153 if new_checksum: 156 if new_checksum:
154 new_image_url = _CreateGSUrl( 157 new_image_url = _CreateGSUrl(
155 imagename=imagename, 158 imagename=imagename,
156 hash_type=gm_json.JSONKEY_HASHTYPE_BITMAP_64BITMD5, 159 hash_type=gm_json.JSONKEY_HASHTYPE_BITMAP_64BITMD5,
157 hash_digest=new_checksum) 160 hash_digest=new_checksum)
158 _DownloadUrlToFile( 161 _DownloadUrlToFile(
159 source_url=new_image_url, 162 source_url=new_image_url,
160 dest_path=os.path.join(new_flattened_dir, 163 dest_path=os.path.join(new_flattened_dir,
161 filename_prefix + imagename)) 164 filename_prefix + imagename))
162 165
166 def _RunCommand(args):
167 """Run a command (from self._directory) and return stdout as a single
168 string.
169
170 @param args a list of arguments
171 """
172 proc = subprocess.Popen(args,
173 stdout=subprocess.PIPE,
174 stderr=subprocess.PIPE)
175 (stdout, stderr) = proc.communicate()
176 if proc.returncode is not 0:
177 raise Exception('command "%s" failed: %s' % (args, stderr))
178 return stdout
179
180 def _GitGetModifiedFiles():
181 """Returns a list of locally modified files within the current working dir.
182
183 TODO(epoger): Move this into a git utility package?
184 """
185 status_regex_string = '^...(\S+)'
186 stdout = _RunCommand(['git', 'status', '-s'])
Stephen White 2013/07/17 10:44:40 GitNit(tm): git status is a "porcelain" command, a
epoger 2013/07/18 15:00:04 Ah, thanks for teaching this git n00b something.
187 status_regex = re.compile(status_regex_string, re.MULTILINE)
188 return status_regex.findall(stdout)
189
190 def _GitExportBaseVersionOfFile(file_within_repo, dest_path):
191 """Retrieves a copy of the base version of a file within the repository.
192
193 @param file_within_repo path to the file within the repo whose base
194 version you wish to obtain
195 @param dest_path destination to which to write the base content
196
197 TODO(epoger): Move this into a git utility package?
198 """
199 args = ['git', 'show', os.path.join('HEAD:.', file_within_repo)]
Stephen White 2013/07/17 10:44:40 Same here. I think the plumbing equivalent is "git
epoger 2013/07/18 15:00:04 I tried "git cat-file" in various permutations, bu
Stephen White 2013/07/18 17:32:38 Sure. Maybe just add a FIXME that we should try to
epoger 2013/07/18 17:40:35 Good idea... done.
200 with open(dest_path, 'wb') as outfile:
201 proc = subprocess.Popen(args, stdout=outfile)
202 proc.communicate()
203 if proc.returncode is not 0:
204 raise Exception('command "%s" failed' % args)
205
163 def SvnDiff(path_to_skdiff, dest_dir, source_dir): 206 def SvnDiff(path_to_skdiff, dest_dir, source_dir):
164 """Generates a visual diff of all pending changes in source_dir. 207 """Generates a visual diff of all pending changes in source_dir.
165 208
166 @param path_to_skdiff 209 @param path_to_skdiff
167 @param dest_dir existing directory within which to write results 210 @param dest_dir existing directory within which to write results
168 @param source_dir 211 @param source_dir
169 """ 212 """
170 # Validate parameters, filling in default values if necessary and possible. 213 # Validate parameters, filling in default values if necessary and possible.
171 path_to_skdiff = os.path.abspath(FindPathToSkDiff(path_to_skdiff)) 214 path_to_skdiff = os.path.abspath(FindPathToSkDiff(path_to_skdiff))
172 if not dest_dir: 215 if not dest_dir:
173 dest_dir = tempfile.mkdtemp() 216 dest_dir = tempfile.mkdtemp()
174 dest_dir = os.path.abspath(dest_dir) 217 dest_dir = os.path.abspath(dest_dir)
175 218
176 os.chdir(source_dir) 219 os.chdir(source_dir)
220 using_svn = os.path.isdir('.svn')
177 221
178 # Prepare temporary directories. 222 # Prepare temporary directories.
179 modified_flattened_dir = os.path.join(dest_dir, 'modified_flattened') 223 modified_flattened_dir = os.path.join(dest_dir, 'modified_flattened')
180 original_flattened_dir = os.path.join(dest_dir, 'original_flattened') 224 original_flattened_dir = os.path.join(dest_dir, 'original_flattened')
181 diff_dir = os.path.join(dest_dir, 'diffs') 225 diff_dir = os.path.join(dest_dir, 'diffs')
182 for dir in [modified_flattened_dir, original_flattened_dir, diff_dir] : 226 for dir in [modified_flattened_dir, original_flattened_dir, diff_dir] :
183 shutil.rmtree(dir, ignore_errors=True) 227 shutil.rmtree(dir, ignore_errors=True)
184 os.mkdir(dir) 228 os.mkdir(dir)
185 229
186 # Get a list of all locally modified (including added/deleted) files, 230 # Get a list of all locally modified (including added/deleted) files,
187 # descending subdirectories. 231 # descending subdirectories.
188 svn_repo = svn.Svn('.') 232 if using_svn:
189 modified_file_paths = svn_repo.GetFilesWithStatus( 233 svn_repo = svn.Svn('.')
190 svn.STATUS_ADDED | svn.STATUS_DELETED | svn.STATUS_MODIFIED) 234 modified_file_paths = svn_repo.GetFilesWithStatus(
235 svn.STATUS_ADDED | svn.STATUS_DELETED | svn.STATUS_MODIFIED)
236 else:
237 modified_file_paths = _GitGetModifiedFiles()
191 238
192 # For each modified file: 239 # For each modified file:
193 # 1. copy its current contents into modified_flattened_dir 240 # 1. copy its current contents into modified_flattened_dir
194 # 2. copy its original contents into original_flattened_dir 241 # 2. copy its original contents into original_flattened_dir
195 for modified_file_path in modified_file_paths: 242 for modified_file_path in modified_file_paths:
196 if modified_file_path.endswith('.json'): 243 if modified_file_path.endswith('.json'):
197 # Special handling for JSON files, in the hopes that they 244 # Special handling for JSON files, in the hopes that they
198 # contain GM result summaries. 245 # contain GM result summaries.
199 (_unused, original_file_path) = tempfile.mkstemp() 246 (_unused, original_file_path) = tempfile.mkstemp()
200 svn_repo.ExportBaseVersionOfFile(modified_file_path, 247 if using_svn:
201 original_file_path) 248 svn_repo.ExportBaseVersionOfFile(
249 modified_file_path, original_file_path)
250 else:
251 _GitExportBaseVersionOfFile(
252 modified_file_path, original_file_path)
202 platform_prefix = re.sub(os.sep, '__', 253 platform_prefix = re.sub(os.sep, '__',
203 os.path.dirname(modified_file_path)) + '__' 254 os.path.dirname(modified_file_path)) + '__'
204 _CallJsonDiff(old_json_path=original_file_path, 255 _CallJsonDiff(old_json_path=original_file_path,
205 new_json_path=modified_file_path, 256 new_json_path=modified_file_path,
206 old_flattened_dir=original_flattened_dir, 257 old_flattened_dir=original_flattened_dir,
207 new_flattened_dir=modified_flattened_dir, 258 new_flattened_dir=modified_flattened_dir,
208 filename_prefix=platform_prefix) 259 filename_prefix=platform_prefix)
209 os.remove(original_file_path) 260 os.remove(original_file_path)
210 else: 261 else:
211 dest_filename = re.sub(os.sep, '__', modified_file_path) 262 dest_filename = re.sub(os.sep, '__', modified_file_path)
212 # If the file had STATUS_DELETED, it won't exist anymore... 263 # If the file had STATUS_DELETED, it won't exist anymore...
213 if os.path.isfile(modified_file_path): 264 if os.path.isfile(modified_file_path):
214 shutil.copyfile(modified_file_path, 265 shutil.copyfile(modified_file_path,
215 os.path.join(modified_flattened_dir, 266 os.path.join(modified_flattened_dir,
216 dest_filename)) 267 dest_filename))
217 svn_repo.ExportBaseVersionOfFile( 268 if using_svn:
218 modified_file_path, 269 svn_repo.ExportBaseVersionOfFile(
219 os.path.join(original_flattened_dir, dest_filename)) 270 modified_file_path,
271 os.path.join(original_flattened_dir, dest_filename))
272 else:
273 _GitExportBaseVersionOfFile(
274 modified_file_path,
275 os.path.join(original_flattened_dir, dest_filename))
220 276
221 # Run skdiff: compare original_flattened_dir against modified_flattened_dir 277 # Run skdiff: compare original_flattened_dir against modified_flattened_dir
222 RunCommand('%s %s %s %s' % (path_to_skdiff, original_flattened_dir, 278 RunCommand('%s %s %s %s' % (path_to_skdiff, original_flattened_dir,
223 modified_flattened_dir, diff_dir)) 279 modified_flattened_dir, diff_dir))
224 print '\nskdiff results are ready in file://%s/index.html' % diff_dir 280 print '\nskdiff results are ready in file://%s/index.html' % diff_dir
225 281
226 def RaiseUsageException(): 282 def RaiseUsageException():
227 raise Exception('%s\nRun with --help for more detail.' % ( 283 raise Exception('%s\nRun with --help for more detail.' % (
228 USAGE_STRING % __file__)) 284 USAGE_STRING % __file__))
229 285
(...skipping 17 matching lines...) Expand all
247 action='store', type='string', default=None, 303 action='store', type='string', default=None,
248 help='path to already-built skdiff tool; if not set, ' 304 help='path to already-built skdiff tool; if not set, '
249 'will search for it in typical directories near this ' 305 'will search for it in typical directories near this '
250 'script') 306 'script')
251 parser.add_option(OPTION_SOURCE_DIR, 307 parser.add_option(OPTION_SOURCE_DIR,
252 action='store', type='string', default='.', 308 action='store', type='string', default='.',
253 help='root directory within which to compare all ' + 309 help='root directory within which to compare all ' +
254 'files; defaults to "%default"') 310 'files; defaults to "%default"')
255 (options, args) = parser.parse_args() 311 (options, args) = parser.parse_args()
256 Main(options, args) 312 Main(options, args)
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