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

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: add_todo 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
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 return _RunCommand(['git', 'ls-files', '-m']).splitlines()
186
187 def _GitExportBaseVersionOfFile(file_within_repo, dest_path):
188 """Retrieves a copy of the base version of a file within the repository.
189
190 @param file_within_repo path to the file within the repo whose base
191 version you wish to obtain
192 @param dest_path destination to which to write the base content
193
194 TODO(epoger): Move this into a git utility package?
195 """
196 # TODO(epoger): Replace use of "git show" command with lower-level git
197 # commands? senorblanco points out that "git show" is a "porcelain"
198 # command, intended for human use, as opposed to the "plumbing" commands
199 # generally more suitable for scripting. (See
200 # http://git-scm.com/book/en/Git-Internals-Plumbing-and-Porcelain )
201 #
202 # For now, though, "git show" is the most straightforward implementation
203 # I could come up with. I tried using "git cat-file", but I had trouble
204 # getting it to work as desired.
205 args = ['git', 'show', os.path.join('HEAD:.', file_within_repo)]
206 with open(dest_path, 'wb') as outfile:
207 proc = subprocess.Popen(args, stdout=outfile)
208 proc.communicate()
209 if proc.returncode is not 0:
210 raise Exception('command "%s" failed' % args)
211
163 def SvnDiff(path_to_skdiff, dest_dir, source_dir): 212 def SvnDiff(path_to_skdiff, dest_dir, source_dir):
164 """Generates a visual diff of all pending changes in source_dir. 213 """Generates a visual diff of all pending changes in source_dir.
165 214
166 @param path_to_skdiff 215 @param path_to_skdiff
167 @param dest_dir existing directory within which to write results 216 @param dest_dir existing directory within which to write results
168 @param source_dir 217 @param source_dir
169 """ 218 """
170 # Validate parameters, filling in default values if necessary and possible. 219 # Validate parameters, filling in default values if necessary and possible.
171 path_to_skdiff = os.path.abspath(FindPathToSkDiff(path_to_skdiff)) 220 path_to_skdiff = os.path.abspath(FindPathToSkDiff(path_to_skdiff))
172 if not dest_dir: 221 if not dest_dir:
173 dest_dir = tempfile.mkdtemp() 222 dest_dir = tempfile.mkdtemp()
174 dest_dir = os.path.abspath(dest_dir) 223 dest_dir = os.path.abspath(dest_dir)
175 224
176 os.chdir(source_dir) 225 os.chdir(source_dir)
226 using_svn = os.path.isdir('.svn')
177 227
178 # Prepare temporary directories. 228 # Prepare temporary directories.
179 modified_flattened_dir = os.path.join(dest_dir, 'modified_flattened') 229 modified_flattened_dir = os.path.join(dest_dir, 'modified_flattened')
180 original_flattened_dir = os.path.join(dest_dir, 'original_flattened') 230 original_flattened_dir = os.path.join(dest_dir, 'original_flattened')
181 diff_dir = os.path.join(dest_dir, 'diffs') 231 diff_dir = os.path.join(dest_dir, 'diffs')
182 for dir in [modified_flattened_dir, original_flattened_dir, diff_dir] : 232 for dir in [modified_flattened_dir, original_flattened_dir, diff_dir] :
183 shutil.rmtree(dir, ignore_errors=True) 233 shutil.rmtree(dir, ignore_errors=True)
184 os.mkdir(dir) 234 os.mkdir(dir)
185 235
186 # Get a list of all locally modified (including added/deleted) files, 236 # Get a list of all locally modified (including added/deleted) files,
187 # descending subdirectories. 237 # descending subdirectories.
188 svn_repo = svn.Svn('.') 238 if using_svn:
189 modified_file_paths = svn_repo.GetFilesWithStatus( 239 svn_repo = svn.Svn('.')
190 svn.STATUS_ADDED | svn.STATUS_DELETED | svn.STATUS_MODIFIED) 240 modified_file_paths = svn_repo.GetFilesWithStatus(
241 svn.STATUS_ADDED | svn.STATUS_DELETED | svn.STATUS_MODIFIED)
242 else:
243 modified_file_paths = _GitGetModifiedFiles()
191 244
192 # For each modified file: 245 # For each modified file:
193 # 1. copy its current contents into modified_flattened_dir 246 # 1. copy its current contents into modified_flattened_dir
194 # 2. copy its original contents into original_flattened_dir 247 # 2. copy its original contents into original_flattened_dir
195 for modified_file_path in modified_file_paths: 248 for modified_file_path in modified_file_paths:
196 if modified_file_path.endswith('.json'): 249 if modified_file_path.endswith('.json'):
197 # Special handling for JSON files, in the hopes that they 250 # Special handling for JSON files, in the hopes that they
198 # contain GM result summaries. 251 # contain GM result summaries.
199 (_unused, original_file_path) = tempfile.mkstemp() 252 (_unused, original_file_path) = tempfile.mkstemp()
200 svn_repo.ExportBaseVersionOfFile(modified_file_path, 253 if using_svn:
201 original_file_path) 254 svn_repo.ExportBaseVersionOfFile(
255 modified_file_path, original_file_path)
256 else:
257 _GitExportBaseVersionOfFile(
258 modified_file_path, original_file_path)
202 platform_prefix = re.sub(os.sep, '__', 259 platform_prefix = re.sub(os.sep, '__',
203 os.path.dirname(modified_file_path)) + '__' 260 os.path.dirname(modified_file_path)) + '__'
204 _CallJsonDiff(old_json_path=original_file_path, 261 _CallJsonDiff(old_json_path=original_file_path,
205 new_json_path=modified_file_path, 262 new_json_path=modified_file_path,
206 old_flattened_dir=original_flattened_dir, 263 old_flattened_dir=original_flattened_dir,
207 new_flattened_dir=modified_flattened_dir, 264 new_flattened_dir=modified_flattened_dir,
208 filename_prefix=platform_prefix) 265 filename_prefix=platform_prefix)
209 os.remove(original_file_path) 266 os.remove(original_file_path)
210 else: 267 else:
211 dest_filename = re.sub(os.sep, '__', modified_file_path) 268 dest_filename = re.sub(os.sep, '__', modified_file_path)
212 # If the file had STATUS_DELETED, it won't exist anymore... 269 # If the file had STATUS_DELETED, it won't exist anymore...
213 if os.path.isfile(modified_file_path): 270 if os.path.isfile(modified_file_path):
214 shutil.copyfile(modified_file_path, 271 shutil.copyfile(modified_file_path,
215 os.path.join(modified_flattened_dir, 272 os.path.join(modified_flattened_dir,
216 dest_filename)) 273 dest_filename))
217 svn_repo.ExportBaseVersionOfFile( 274 if using_svn:
218 modified_file_path, 275 svn_repo.ExportBaseVersionOfFile(
219 os.path.join(original_flattened_dir, dest_filename)) 276 modified_file_path,
277 os.path.join(original_flattened_dir, dest_filename))
278 else:
279 _GitExportBaseVersionOfFile(
280 modified_file_path,
281 os.path.join(original_flattened_dir, dest_filename))
220 282
221 # Run skdiff: compare original_flattened_dir against modified_flattened_dir 283 # Run skdiff: compare original_flattened_dir against modified_flattened_dir
222 RunCommand('%s %s %s %s' % (path_to_skdiff, original_flattened_dir, 284 RunCommand('%s %s %s %s' % (path_to_skdiff, original_flattened_dir,
223 modified_flattened_dir, diff_dir)) 285 modified_flattened_dir, diff_dir))
224 print '\nskdiff results are ready in file://%s/index.html' % diff_dir 286 print '\nskdiff results are ready in file://%s/index.html' % diff_dir
225 287
226 def RaiseUsageException(): 288 def RaiseUsageException():
227 raise Exception('%s\nRun with --help for more detail.' % ( 289 raise Exception('%s\nRun with --help for more detail.' % (
228 USAGE_STRING % __file__)) 290 USAGE_STRING % __file__))
229 291
(...skipping 17 matching lines...) Expand all
247 action='store', type='string', default=None, 309 action='store', type='string', default=None,
248 help='path to already-built skdiff tool; if not set, ' 310 help='path to already-built skdiff tool; if not set, '
249 'will search for it in typical directories near this ' 311 'will search for it in typical directories near this '
250 'script') 312 'script')
251 parser.add_option(OPTION_SOURCE_DIR, 313 parser.add_option(OPTION_SOURCE_DIR,
252 action='store', type='string', default='.', 314 action='store', type='string', default='.',
253 help='root directory within which to compare all ' + 315 help='root directory within which to compare all ' +
254 'files; defaults to "%default"') 316 'files; defaults to "%default"')
255 (options, args) = parser.parse_args() 317 (options, args) = parser.parse_args()
256 Main(options, args) 318 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