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 |