Chromium Code Reviews| 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 for svn, git-svn and git. | 7 Includes support for svn, git-svn and git. |
| 8 """ | 8 """ |
| 9 | 9 |
| 10 import ConfigParser | 10 import ConfigParser |
| (...skipping 114 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 125 | 125 |
| 126 This function shouldn't throw unless the remote repository is inaccessible, | 126 This function shouldn't throw unless the remote repository is inaccessible, |
| 127 there is no free disk space or hard issues like that. | 127 there is no free disk space or hard issues like that. |
| 128 | 128 |
| 129 Args: | 129 Args: |
| 130 revision: The revision it should sync to, SCM specific. | 130 revision: The revision it should sync to, SCM specific. |
| 131 """ | 131 """ |
| 132 raise NotImplementedError() | 132 raise NotImplementedError() |
| 133 | 133 |
| 134 def apply_patch(self, patches, post_processors=None, verbose=False, | 134 def apply_patch(self, patches, post_processors=None, verbose=False, |
| 135 name=None, email=None): | 135 name=None, email=None, whitelist=None, blacklist=None): |
|
M-A Ruel
2014/05/06 18:49:08
I think you are doing it at the wrong abstraction
Ryan Tseng
2014/05/06 19:31:53
Done.
| |
| 136 """Applies a patch and returns the list of modified files. | 136 """Applies a patch and returns the list of modified files. |
| 137 | 137 |
| 138 This function should throw patch.UnsupportedPatchFormat or | 138 This function should throw patch.UnsupportedPatchFormat or |
| 139 PatchApplicationFailed when relevant. | 139 PatchApplicationFailed when relevant. |
| 140 | 140 |
| 141 Args: | 141 Args: |
| 142 patches: patch.PatchSet object. | 142 patches: patch.PatchSet object. |
| 143 """ | 143 """ |
| 144 raise NotImplementedError() | 144 raise NotImplementedError() |
| 145 | 145 |
| (...skipping 14 matching lines...) Expand all Loading... | |
| 160 class RawCheckout(CheckoutBase): | 160 class RawCheckout(CheckoutBase): |
| 161 """Used to apply a patch locally without any intent to commit it. | 161 """Used to apply a patch locally without any intent to commit it. |
| 162 | 162 |
| 163 To be used by the try server. | 163 To be used by the try server. |
| 164 """ | 164 """ |
| 165 def prepare(self, revision): | 165 def prepare(self, revision): |
| 166 """Stubbed out.""" | 166 """Stubbed out.""" |
| 167 pass | 167 pass |
| 168 | 168 |
| 169 def apply_patch(self, patches, post_processors=None, verbose=False, | 169 def apply_patch(self, patches, post_processors=None, verbose=False, |
| 170 name=None, email=None): | 170 name=None, email=None, whitelist=None, blacklist=None): |
| 171 """Ignores svn properties.""" | 171 """Ignores svn properties.""" |
| 172 post_processors = post_processors or self.post_processors or [] | 172 post_processors = post_processors or self.post_processors or [] |
| 173 for p in patches: | 173 for p in patches: |
| 174 stdout = [] | 174 stdout = [] |
| 175 try: | 175 try: |
| 176 filepath = os.path.join(self.project_path, p.filename) | 176 filepath = os.path.join(self.project_path, p.filename) |
| 177 if blacklist and p.filename in blacklist: | |
| 178 continue | |
| 179 if whitelist and p.filename not in whitelist: | |
| 180 continue | |
| 177 if p.is_delete: | 181 if p.is_delete: |
| 178 os.remove(filepath) | 182 os.remove(filepath) |
| 179 stdout.append('Deleted.') | 183 stdout.append('Deleted.') |
| 180 else: | 184 else: |
| 181 dirname = os.path.dirname(p.filename) | 185 dirname = os.path.dirname(p.filename) |
| 182 full_dir = os.path.join(self.project_path, dirname) | 186 full_dir = os.path.join(self.project_path, dirname) |
| 183 if dirname and not os.path.isdir(full_dir): | 187 if dirname and not os.path.isdir(full_dir): |
| 184 os.makedirs(full_dir) | 188 os.makedirs(full_dir) |
| 185 stdout.append('Created missing directory %s.' % dirname) | 189 stdout.append('Created missing directory %s.' % dirname) |
| 186 | 190 |
| (...skipping 158 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 345 | 349 |
| 346 def prepare(self, revision): | 350 def prepare(self, revision): |
| 347 # Will checkout if the directory is not present. | 351 # Will checkout if the directory is not present. |
| 348 assert self.svn_url | 352 assert self.svn_url |
| 349 if not os.path.isdir(self.project_path): | 353 if not os.path.isdir(self.project_path): |
| 350 logging.info('Checking out %s in %s' % | 354 logging.info('Checking out %s in %s' % |
| 351 (self.project_name, self.project_path)) | 355 (self.project_name, self.project_path)) |
| 352 return self._revert(revision) | 356 return self._revert(revision) |
| 353 | 357 |
| 354 def apply_patch(self, patches, post_processors=None, verbose=False, | 358 def apply_patch(self, patches, post_processors=None, verbose=False, |
| 355 name=None, email=None): | 359 name=None, email=None, whitelist=None, blacklist=None): |
| 356 post_processors = post_processors or self.post_processors or [] | 360 post_processors = post_processors or self.post_processors or [] |
| 357 for p in patches: | 361 for p in patches: |
| 358 stdout = [] | 362 stdout = [] |
| 359 try: | 363 try: |
| 364 if blacklist and p.filename in blacklist: | |
| 365 continue | |
| 366 if whitelist and p.filename not in whitelist: | |
| 367 continue | |
| 360 filepath = os.path.join(self.project_path, p.filename) | 368 filepath = os.path.join(self.project_path, p.filename) |
| 361 # It is important to use credentials=False otherwise credentials could | 369 # It is important to use credentials=False otherwise credentials could |
| 362 # leak in the error message. Credentials are not necessary here for the | 370 # leak in the error message. Credentials are not necessary here for the |
| 363 # following commands anyway. | 371 # following commands anyway. |
| 364 if p.is_delete: | 372 if p.is_delete: |
| 365 stdout.append(self._check_output_svn( | 373 stdout.append(self._check_output_svn( |
| 366 ['delete', p.filename, '--force'], credentials=False)) | 374 ['delete', p.filename, '--force'], credentials=False)) |
| 367 stdout.append('Deleted.') | 375 stdout.append('Deleted.') |
| 368 else: | 376 else: |
| 369 # svn add while creating directories otherwise svn add on the | 377 # svn add while creating directories otherwise svn add on the |
| (...skipping 255 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 625 self._check_call_git( | 633 self._check_call_git( |
| 626 ['pull', self.remote, | 634 ['pull', self.remote, |
| 627 '%s:%s' % (self.remote_branch, remote_tracked_path), | 635 '%s:%s' % (self.remote_branch, remote_tracked_path), |
| 628 '--quiet']) | 636 '--quiet']) |
| 629 | 637 |
| 630 def _get_head_commit_hash(self): | 638 def _get_head_commit_hash(self): |
| 631 """Gets the current revision (in unicode) from the local branch.""" | 639 """Gets the current revision (in unicode) from the local branch.""" |
| 632 return unicode(self._check_output_git(['rev-parse', 'HEAD']).strip()) | 640 return unicode(self._check_output_git(['rev-parse', 'HEAD']).strip()) |
| 633 | 641 |
| 634 def apply_patch(self, patches, post_processors=None, verbose=False, | 642 def apply_patch(self, patches, post_processors=None, verbose=False, |
| 635 name=None, email=None): | 643 name=None, email=None, whitelist=None, blacklist=None): |
| 636 """Applies a patch on 'working_branch' and switches to it. | 644 """Applies a patch on 'working_branch' and switches to it. |
| 637 | 645 |
| 638 Also commits the changes on the local branch. | 646 Also commits the changes on the local branch. |
| 639 | 647 |
| 648 If ignore_deps is set, then files matching deps will not get patched. | |
| 649 If deps_only is set, then only files matching deps will get patched. | |
| 650 | |
| 640 Ignores svn properties and raise an exception on unexpected ones. | 651 Ignores svn properties and raise an exception on unexpected ones. |
| 641 """ | 652 """ |
| 642 post_processors = post_processors or self.post_processors or [] | 653 post_processors = post_processors or self.post_processors or [] |
| 643 # It this throws, the checkout is corrupted. Maybe worth deleting it and | 654 # It this throws, the checkout is corrupted. Maybe worth deleting it and |
| 644 # trying again? | 655 # trying again? |
| 645 if self.remote_branch: | 656 if self.remote_branch: |
| 646 self._check_call_git( | 657 self._check_call_git( |
| 647 ['checkout', '-b', self.working_branch, '-t', self.remote_branch, | 658 ['checkout', '-b', self.working_branch, '-t', self.remote_branch, |
| 648 '--quiet']) | 659 '--quiet']) |
| 649 | 660 |
| 650 for index, p in enumerate(patches): | 661 for index, p in enumerate(patches): |
| 651 stdout = [] | 662 stdout = [] |
| 652 try: | 663 try: |
| 664 if blacklist and p.filename in blacklist: | |
| 665 continue | |
| 666 if whitelist and p.filename not in whitelist: | |
| 667 continue | |
| 653 filepath = os.path.join(self.project_path, p.filename) | 668 filepath = os.path.join(self.project_path, p.filename) |
| 654 if p.is_delete: | 669 if p.is_delete: |
| 655 if (not os.path.exists(filepath) and | 670 if (not os.path.exists(filepath) and |
| 656 any(p1.source_filename == p.filename for p1 in patches[0:index])): | 671 any(p1.source_filename == p.filename for p1 in patches[0:index])): |
| 657 # The file was already deleted if a prior patch with file rename | 672 # The file was already deleted if a prior patch with file rename |
| 658 # was already processed because 'git apply' did it for us. | 673 # was already processed because 'git apply' did it for us. |
| 659 pass | 674 pass |
| 660 else: | 675 else: |
| 661 stdout.append(self._check_output_git(['rm', p.filename])) | 676 stdout.append(self._check_output_git(['rm', p.filename])) |
| 662 stdout.append('Deleted.') | 677 stdout.append('Deleted.') |
| (...skipping 19 matching lines...) Expand all Loading... | |
| 682 if verbose: | 697 if verbose: |
| 683 cmd.append('--verbose') | 698 cmd.append('--verbose') |
| 684 stdout.append(self._check_output_git(cmd, stdin=p.get(True))) | 699 stdout.append(self._check_output_git(cmd, stdin=p.get(True))) |
| 685 for name, value in p.svn_properties: | 700 for name, value in p.svn_properties: |
| 686 # Ignore some known auto-props flags through .subversion/config, | 701 # Ignore some known auto-props flags through .subversion/config, |
| 687 # bails out on the other ones. | 702 # bails out on the other ones. |
| 688 # TODO(maruel): Read ~/.subversion/config and detect the rules that | 703 # TODO(maruel): Read ~/.subversion/config and detect the rules that |
| 689 # applies here to figure out if the property will be correctly | 704 # applies here to figure out if the property will be correctly |
| 690 # handled. | 705 # handled. |
| 691 stdout.append('Property %s=%s' % (name, value)) | 706 stdout.append('Property %s=%s' % (name, value)) |
| 707 # TODO(hinoka): Also remove the executable flag if svn:executabe | |
| 708 # is not set yet the file is executable. | |
| 692 if not name in ( | 709 if not name in ( |
| 693 'svn:eol-style', 'svn:executable', 'svn:mime-type'): | 710 'svn:eol-style', 'svn:executable', 'svn:mime-type'): |
| 694 raise patch.UnsupportedPatchFormat( | 711 raise patch.UnsupportedPatchFormat( |
| 695 p.filename, | 712 p.filename, |
| 696 'Cannot apply svn property %s to file %s.' % ( | 713 'Cannot apply svn property %s to file %s.' % ( |
| 697 name, p.filename)) | 714 name, p.filename)) |
| 698 for post in post_processors: | 715 for post in post_processors: |
| 699 post(self, p) | 716 post(self, p) |
| 700 if verbose: | 717 if verbose: |
| 701 print p.filename | 718 print p.filename |
| (...skipping 16 matching lines...) Expand all Loading... | |
| 718 cmd.append('--verbose') | 735 cmd.append('--verbose') |
| 719 self._check_call_git(cmd) | 736 self._check_call_git(cmd) |
| 720 if self.base_ref: | 737 if self.base_ref: |
| 721 base_ref = self.base_ref | 738 base_ref = self.base_ref |
| 722 else: | 739 else: |
| 723 base_ref = '%s/%s' % (self.remote, | 740 base_ref = '%s/%s' % (self.remote, |
| 724 self.remote_branch or self.master_branch) | 741 self.remote_branch or self.master_branch) |
| 725 found_files = self._check_output_git( | 742 found_files = self._check_output_git( |
| 726 ['diff', base_ref, '--ignore-submodules', | 743 ['diff', base_ref, '--ignore-submodules', |
| 727 '--name-only']).splitlines(False) | 744 '--name-only']).splitlines(False) |
| 728 assert sorted(patches.filenames) == sorted(found_files), ( | 745 expected_files = patches.filenames |
| 729 sorted(patches.filenames), sorted(found_files)) | 746 if whitelist: |
| 747 expected_files = whitelist | |
| 748 if blacklist: | |
| 749 expected_files = [f for f in expected_files if f not in blacklist] | |
| 750 assert sorted(expected_files) == sorted(found_files), ( | |
| 751 sorted(expected_files), sorted(found_files)) | |
| 730 | 752 |
| 731 def commit(self, commit_message, user): | 753 def commit(self, commit_message, user): |
| 732 """Commits, updates the commit message and pushes.""" | 754 """Commits, updates the commit message and pushes.""" |
| 733 assert self.commit_user | 755 assert self.commit_user |
| 734 assert isinstance(commit_message, unicode) | 756 assert isinstance(commit_message, unicode) |
| 735 current_branch = self._check_output_git( | 757 current_branch = self._check_output_git( |
| 736 ['rev-parse', '--abbrev-ref', 'HEAD']).strip() | 758 ['rev-parse', '--abbrev-ref', 'HEAD']).strip() |
| 737 assert current_branch == self.working_branch | 759 assert current_branch == self.working_branch |
| 738 | 760 |
| 739 commit_cmd = ['commit', '--amend', '-m', commit_message] | 761 commit_cmd = ['commit', '--amend', '-m', commit_message] |
| (...skipping 78 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 818 self.post_processors = (post_processors or []) + ( | 840 self.post_processors = (post_processors or []) + ( |
| 819 self.checkout.post_processors or []) | 841 self.checkout.post_processors or []) |
| 820 | 842 |
| 821 def prepare(self, revision): | 843 def prepare(self, revision): |
| 822 return self.checkout.prepare(revision) | 844 return self.checkout.prepare(revision) |
| 823 | 845 |
| 824 def get_settings(self, key): | 846 def get_settings(self, key): |
| 825 return self.checkout.get_settings(key) | 847 return self.checkout.get_settings(key) |
| 826 | 848 |
| 827 def apply_patch(self, patches, post_processors=None, verbose=False, | 849 def apply_patch(self, patches, post_processors=None, verbose=False, |
| 828 name=None, email=None): | 850 name=None, email=None, whitelist=None, blacklist=None): |
| 851 if not whitelist: | |
| 852 whitelist = [] | |
| 853 if not blacklist: | |
| 854 blacklist = [] | |
| 829 return self.checkout.apply_patch( | 855 return self.checkout.apply_patch( |
| 830 patches, post_processors or self.post_processors, verbose) | 856 patches, post_processors or self.post_processors, verbose, |
| 857 whitelist=whitelist, | |
| 858 blacklist=blacklist) | |
| 831 | 859 |
| 832 def commit(self, message, user): # pylint: disable=R0201 | 860 def commit(self, message, user): # pylint: disable=R0201 |
| 833 logging.info('Would have committed for %s with message: %s' % ( | 861 logging.info('Would have committed for %s with message: %s' % ( |
| 834 user, message)) | 862 user, message)) |
| 835 return 'FAKE' | 863 return 'FAKE' |
| 836 | 864 |
| 837 def revisions(self, rev1, rev2): | 865 def revisions(self, rev1, rev2): |
| 838 return self.checkout.revisions(rev1, rev2) | 866 return self.checkout.revisions(rev1, rev2) |
| 839 | 867 |
| 840 @property | 868 @property |
| 841 def project_name(self): | 869 def project_name(self): |
| 842 return self.checkout.project_name | 870 return self.checkout.project_name |
| 843 | 871 |
| 844 @property | 872 @property |
| 845 def project_path(self): | 873 def project_path(self): |
| 846 return self.checkout.project_path | 874 return self.checkout.project_path |
| OLD | NEW |