| OLD | NEW |
| 1 # coding=utf8 | 1 # coding=utf8 |
| 2 # Copyright (c) 2012 The Chromium Authors. All rights reserved. | 2 # Copyright (c) 2012 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 """Manages a project checkout. | 5 """Manages a project checkout. |
| 6 | 6 |
| 7 Includes support only for git. | 7 Includes support only for git. |
| 8 """ | 8 """ |
| 9 | 9 |
| 10 import fnmatch | 10 import fnmatch |
| (...skipping 59 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 70 for item in stdout: | 70 for item in stdout: |
| 71 item = item.strip() | 71 item = item.strip() |
| 72 if not item: | 72 if not item: |
| 73 continue | 73 continue |
| 74 output += ''.join(' %s\n' % line for line in item.splitlines()) | 74 output += ''.join(' %s\n' % line for line in item.splitlines()) |
| 75 return output | 75 return output |
| 76 | 76 |
| 77 | 77 |
| 78 class PatchApplicationFailed(Exception): | 78 class PatchApplicationFailed(Exception): |
| 79 """Patch failed to be applied.""" | 79 """Patch failed to be applied.""" |
| 80 def __init__(self, p, status): | 80 def __init__(self, errors, verbose): |
| 81 super(PatchApplicationFailed, self).__init__(p, status) | 81 super(PatchApplicationFailed, self).__init__(errors, verbose) |
| 82 self.patch = p | 82 self.errors = errors |
| 83 self.status = status | 83 self.verbose = verbose |
| 84 | |
| 85 @property | |
| 86 def filename(self): | |
| 87 if self.patch: | |
| 88 return self.patch.filename | |
| 89 | 84 |
| 90 def __str__(self): | 85 def __str__(self): |
| 91 out = [] | 86 out = [] |
| 92 if self.filename: | 87 for e in self.errors: |
| 93 out.append('Failed to apply patch for %s:' % self.filename) | 88 p, status = e |
| 94 if self.status: | 89 if p and p.filename: |
| 95 out.append(self.status) | 90 out.append('Failed to apply patch for %s:' % p.filename) |
| 96 if self.patch: | 91 if status: |
| 97 out.append('Patch: %s' % self.patch.dump()) | 92 out.append(status) |
| 93 if p and self.verbose: |
| 94 out.append('Patch: %s' % p.dump()) |
| 98 return '\n'.join(out) | 95 return '\n'.join(out) |
| 99 | 96 |
| 100 | 97 |
| 101 class CheckoutBase(object): | 98 class CheckoutBase(object): |
| 102 # Set to None to have verbose output. | 99 # Set to None to have verbose output. |
| 103 VOID = subprocess2.VOID | 100 VOID = subprocess2.VOID |
| 104 | 101 |
| 105 def __init__(self, root_dir, project_name, post_processors): | 102 def __init__(self, root_dir, project_name, post_processors): |
| 106 """ | 103 """ |
| 107 Args: | 104 Args: |
| (...skipping 136 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 244 The changes remain staged on the current branch. | 241 The changes remain staged on the current branch. |
| 245 """ | 242 """ |
| 246 post_processors = post_processors or self.post_processors or [] | 243 post_processors = post_processors or self.post_processors or [] |
| 247 # It this throws, the checkout is corrupted. Maybe worth deleting it and | 244 # It this throws, the checkout is corrupted. Maybe worth deleting it and |
| 248 # trying again? | 245 # trying again? |
| 249 if self.remote_branch: | 246 if self.remote_branch: |
| 250 self._check_call_git( | 247 self._check_call_git( |
| 251 ['checkout', '-b', self.working_branch, '-t', self.remote_branch, | 248 ['checkout', '-b', self.working_branch, '-t', self.remote_branch, |
| 252 '--quiet']) | 249 '--quiet']) |
| 253 | 250 |
| 251 errors = [] |
| 254 for index, p in enumerate(patches): | 252 for index, p in enumerate(patches): |
| 255 stdout = [] | 253 stdout = [] |
| 256 try: | 254 try: |
| 257 filepath = os.path.join(self.project_path, p.filename) | 255 filepath = os.path.join(self.project_path, p.filename) |
| 258 if p.is_delete: | 256 if p.is_delete: |
| 259 if (not os.path.exists(filepath) and | 257 if (not os.path.exists(filepath) and |
| 260 any(p1.source_filename == p.filename for p1 in patches[0:index])): | 258 any(p1.source_filename == p.filename for p1 in patches[0:index])): |
| 261 # The file was already deleted if a prior patch with file rename | 259 # The file was already deleted if a prior patch with file rename |
| 262 # was already processed because 'git apply' did it for us. | 260 # was already processed because 'git apply' did it for us. |
| 263 pass | 261 pass |
| (...skipping 22 matching lines...) Expand all Loading... |
| 286 cmd = ['apply', '--index', '-3', '-p%s' % p.patchlevel] | 284 cmd = ['apply', '--index', '-3', '-p%s' % p.patchlevel] |
| 287 if verbose: | 285 if verbose: |
| 288 cmd.append('--verbose') | 286 cmd.append('--verbose') |
| 289 stdout.append(self._check_output_git(cmd, stdin=p.get(True))) | 287 stdout.append(self._check_output_git(cmd, stdin=p.get(True))) |
| 290 for post in post_processors: | 288 for post in post_processors: |
| 291 post(self, p) | 289 post(self, p) |
| 292 if verbose: | 290 if verbose: |
| 293 print p.filename | 291 print p.filename |
| 294 print align_stdout(stdout) | 292 print align_stdout(stdout) |
| 295 except OSError, e: | 293 except OSError, e: |
| 296 raise PatchApplicationFailed(p, '%s%s' % (align_stdout(stdout), e)) | 294 errors.append((p, '%s%s' % (align_stdout(stdout), e))) |
| 297 except subprocess.CalledProcessError, e: | 295 except subprocess.CalledProcessError, e: |
| 298 raise PatchApplicationFailed( | 296 errors.append((p, |
| 299 p, | |
| 300 'While running %s;\n%s%s' % ( | 297 'While running %s;\n%s%s' % ( |
| 301 ' '.join(e.cmd), | 298 ' '.join(e.cmd), |
| 302 align_stdout(stdout), | 299 align_stdout(stdout), |
| 303 align_stdout([getattr(e, 'stdout', '')]))) | 300 align_stdout([getattr(e, 'stdout', '')])))) |
| 301 if errors: |
| 302 raise PatchApplicationFailed(errors, verbose) |
| 304 found_files = self._check_output_git( | 303 found_files = self._check_output_git( |
| 305 ['diff', '--ignore-submodules', | 304 ['diff', '--ignore-submodules', |
| 306 '--name-only', '--staged']).splitlines(False) | 305 '--name-only', '--staged']).splitlines(False) |
| 307 if sorted(patches.filenames) != sorted(found_files): | 306 if sorted(patches.filenames) != sorted(found_files): |
| 308 extra_files = sorted(set(found_files) - set(patches.filenames)) | 307 extra_files = sorted(set(found_files) - set(patches.filenames)) |
| 309 unpatched_files = sorted(set(patches.filenames) - set(found_files)) | 308 unpatched_files = sorted(set(patches.filenames) - set(found_files)) |
| 310 if extra_files: | 309 if extra_files: |
| 311 print 'Found extra files: %r' % (extra_files,) | 310 print 'Found extra files: %r' % (extra_files,) |
| 312 if unpatched_files: | 311 if unpatched_files: |
| 313 print 'Found unpatched files: %r' % (unpatched_files,) | 312 print 'Found unpatched files: %r' % (unpatched_files,) |
| (...skipping 109 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 423 def revisions(self, rev1, rev2): | 422 def revisions(self, rev1, rev2): |
| 424 return self.checkout.revisions(rev1, rev2) | 423 return self.checkout.revisions(rev1, rev2) |
| 425 | 424 |
| 426 @property | 425 @property |
| 427 def project_name(self): | 426 def project_name(self): |
| 428 return self.checkout.project_name | 427 return self.checkout.project_name |
| 429 | 428 |
| 430 @property | 429 @property |
| 431 def project_path(self): | 430 def project_path(self): |
| 432 return self.checkout.project_path | 431 return self.checkout.project_path |
| OLD | NEW |