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 113 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
124 """Checks out a clean copy of the tree and removes any local modification. | 124 """Checks out a clean copy of the tree and removes any local modification. |
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): | |
136 """Applies a patch and returns the list of modified files. | 135 """Applies a patch and returns the list of modified files. |
137 | 136 |
138 This function should throw patch.UnsupportedPatchFormat or | 137 This function should throw patch.UnsupportedPatchFormat or |
139 PatchApplicationFailed when relevant. | 138 PatchApplicationFailed when relevant. |
140 | 139 |
141 Args: | 140 Args: |
142 patches: patch.PatchSet object. | 141 patches: patch.PatchSet object. |
143 """ | 142 """ |
144 raise NotImplementedError() | 143 raise NotImplementedError() |
145 | 144 |
(...skipping 13 matching lines...) Expand all Loading... | |
159 | 158 |
160 class RawCheckout(CheckoutBase): | 159 class RawCheckout(CheckoutBase): |
161 """Used to apply a patch locally without any intent to commit it. | 160 """Used to apply a patch locally without any intent to commit it. |
162 | 161 |
163 To be used by the try server. | 162 To be used by the try server. |
164 """ | 163 """ |
165 def prepare(self, revision): | 164 def prepare(self, revision): |
166 """Stubbed out.""" | 165 """Stubbed out.""" |
167 pass | 166 pass |
168 | 167 |
169 def apply_patch(self, patches, post_processors=None, verbose=False, | 168 def apply_patch(self, patches, post_processors=None, verbose=False): |
170 name=None, email=None): | |
171 """Ignores svn properties.""" | 169 """Ignores svn properties.""" |
172 post_processors = post_processors or self.post_processors or [] | 170 post_processors = post_processors or self.post_processors or [] |
173 for p in patches: | 171 for p in patches: |
174 stdout = [] | 172 stdout = [] |
175 try: | 173 try: |
176 filepath = os.path.join(self.project_path, p.filename) | 174 filepath = os.path.join(self.project_path, p.filename) |
177 if p.is_delete: | 175 if p.is_delete: |
178 os.remove(filepath) | 176 os.remove(filepath) |
179 stdout.append('Deleted.') | 177 stdout.append('Deleted.') |
180 else: | 178 else: |
(...skipping 163 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
344 assert bool(self.commit_user) >= bool(self.commit_pwd) | 342 assert bool(self.commit_user) >= bool(self.commit_pwd) |
345 | 343 |
346 def prepare(self, revision): | 344 def prepare(self, revision): |
347 # Will checkout if the directory is not present. | 345 # Will checkout if the directory is not present. |
348 assert self.svn_url | 346 assert self.svn_url |
349 if not os.path.isdir(self.project_path): | 347 if not os.path.isdir(self.project_path): |
350 logging.info('Checking out %s in %s' % | 348 logging.info('Checking out %s in %s' % |
351 (self.project_name, self.project_path)) | 349 (self.project_name, self.project_path)) |
352 return self._revert(revision) | 350 return self._revert(revision) |
353 | 351 |
354 def apply_patch(self, patches, post_processors=None, verbose=False, | 352 def apply_patch(self, patches, post_processors=None, verbose=False): |
355 name=None, email=None): | |
356 post_processors = post_processors or self.post_processors or [] | 353 post_processors = post_processors or self.post_processors or [] |
357 for p in patches: | 354 for p in patches: |
358 stdout = [] | 355 stdout = [] |
359 try: | 356 try: |
360 filepath = os.path.join(self.project_path, p.filename) | 357 filepath = os.path.join(self.project_path, p.filename) |
361 # It is important to use credentials=False otherwise credentials could | 358 # It is important to use credentials=False otherwise credentials could |
362 # leak in the error message. Credentials are not necessary here for the | 359 # leak in the error message. Credentials are not necessary here for the |
363 # following commands anyway. | 360 # following commands anyway. |
364 if p.is_delete: | 361 if p.is_delete: |
365 stdout.append(self._check_output_svn( | 362 stdout.append(self._check_output_svn( |
(...skipping 183 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
549 ['log', '-q', self.svn_url, '-r', '%s:%s' % (rev1, rev2)]) | 546 ['log', '-q', self.svn_url, '-r', '%s:%s' % (rev1, rev2)]) |
550 except subprocess.CalledProcessError: | 547 except subprocess.CalledProcessError: |
551 return None | 548 return None |
552 # Ignore the '----' lines. | 549 # Ignore the '----' lines. |
553 return len([l for l in out.splitlines() if l.startswith('r')]) - 1 | 550 return len([l for l in out.splitlines() if l.startswith('r')]) - 1 |
554 | 551 |
555 | 552 |
556 class GitCheckout(CheckoutBase): | 553 class GitCheckout(CheckoutBase): |
557 """Manages a git checkout.""" | 554 """Manages a git checkout.""" |
558 def __init__(self, root_dir, project_name, remote_branch, git_url, | 555 def __init__(self, root_dir, project_name, remote_branch, git_url, |
559 commit_user, post_processors=None, base_ref=None): | 556 commit_user, post_processors=None): |
560 super(GitCheckout, self).__init__(root_dir, project_name, post_processors) | 557 super(GitCheckout, self).__init__(root_dir, project_name, post_processors) |
561 self.base_ref = base_ref | |
562 self.git_url = git_url | 558 self.git_url = git_url |
563 self.commit_user = commit_user | 559 self.commit_user = commit_user |
564 self.remote_branch = remote_branch | 560 self.remote_branch = remote_branch |
565 # The working branch where patches will be applied. It will track the | 561 # The working branch where patches will be applied. It will track the |
566 # remote branch. | 562 # remote branch. |
567 self.working_branch = 'working_branch' | 563 self.working_branch = 'working_branch' |
568 # There is no reason to not hardcode origin. | 564 # There is no reason to not hardcode origin. |
569 self.remote = 'origin' | 565 self.remote = 'origin' |
570 # There is no reason to not hardcode master. | 566 # There is no reason to not hardcode master. |
571 self.master_branch = 'master' | 567 self.master_branch = 'master' |
(...skipping 52 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
624 self.remote, self.remote_branch) | 620 self.remote, self.remote_branch) |
625 self._check_call_git( | 621 self._check_call_git( |
626 ['pull', self.remote, | 622 ['pull', self.remote, |
627 '%s:%s' % (self.remote_branch, remote_tracked_path), | 623 '%s:%s' % (self.remote_branch, remote_tracked_path), |
628 '--quiet']) | 624 '--quiet']) |
629 | 625 |
630 def _get_head_commit_hash(self): | 626 def _get_head_commit_hash(self): |
631 """Gets the current revision (in unicode) from the local branch.""" | 627 """Gets the current revision (in unicode) from the local branch.""" |
632 return unicode(self._check_output_git(['rev-parse', 'HEAD']).strip()) | 628 return unicode(self._check_output_git(['rev-parse', 'HEAD']).strip()) |
633 | 629 |
634 def apply_patch(self, patches, post_processors=None, verbose=False, | 630 def apply_patch(self, patches, post_processors=None, verbose=False): |
iannucci
2014/05/08 07:29:48
(also, this code terrifies me to no end. There is
M-A Ruel
2014/05/08 16:40:18
It is simple;
- It properly applies svn generated
| |
635 name=None, email=None): | |
636 """Applies a patch on 'working_branch' and switches to it. | 631 """Applies a patch on 'working_branch' and switches to it. |
637 | 632 |
638 Also commits the changes on the local branch. | 633 The changes remain staged on the current branch. |
639 | 634 |
640 Ignores svn properties and raise an exception on unexpected ones. | 635 Ignores svn properties and raise an exception on unexpected ones. |
641 """ | 636 """ |
642 post_processors = post_processors or self.post_processors or [] | 637 post_processors = post_processors or self.post_processors or [] |
643 # It this throws, the checkout is corrupted. Maybe worth deleting it and | 638 # It this throws, the checkout is corrupted. Maybe worth deleting it and |
644 # trying again? | 639 # trying again? |
645 if self.remote_branch: | 640 if self.remote_branch: |
646 self._check_call_git( | 641 self._check_call_git( |
647 ['checkout', '-b', self.working_branch, '-t', self.remote_branch, | 642 ['checkout', '-b', self.working_branch, '-t', self.remote_branch, |
648 '--quiet']) | 643 '--quiet']) |
(...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
702 print align_stdout(stdout) | 697 print align_stdout(stdout) |
703 except OSError, e: | 698 except OSError, e: |
704 raise PatchApplicationFailed(p, '%s%s' % (align_stdout(stdout), e)) | 699 raise PatchApplicationFailed(p, '%s%s' % (align_stdout(stdout), e)) |
705 except subprocess.CalledProcessError, e: | 700 except subprocess.CalledProcessError, e: |
706 raise PatchApplicationFailed( | 701 raise PatchApplicationFailed( |
707 p, | 702 p, |
708 'While running %s;\n%s%s' % ( | 703 'While running %s;\n%s%s' % ( |
709 ' '.join(e.cmd), | 704 ' '.join(e.cmd), |
710 align_stdout(stdout), | 705 align_stdout(stdout), |
711 align_stdout([getattr(e, 'stdout', '')]))) | 706 align_stdout([getattr(e, 'stdout', '')]))) |
712 # Once all the patches are processed and added to the index, commit the | |
713 # index. | |
714 cmd = ['commit', '-m', 'Committed patch'] | |
715 if name and email: | |
716 cmd = ['-c', 'user.email=%s' % email, '-c', 'user.name=%s' % name] + cmd | |
717 if verbose: | |
718 cmd.append('--verbose') | |
719 self._check_call_git(cmd) | |
720 if self.base_ref: | |
721 base_ref = self.base_ref | |
722 else: | |
723 base_ref = '%s/%s' % (self.remote, | |
724 self.remote_branch or self.master_branch) | |
725 found_files = self._check_output_git( | 707 found_files = self._check_output_git( |
726 ['diff', base_ref, '--ignore-submodules', | 708 ['diff', '--ignore-submodules', |
727 '--name-only']).splitlines(False) | 709 '--name-only', '--staged']).splitlines(False) |
728 assert sorted(patches.filenames) == sorted(found_files), ( | 710 assert sorted(patches.filenames) == sorted(found_files), ( |
729 'Found extra %s locally, %s not patched' % ( | 711 'Found extra %s locally, %s not patched' % ( |
730 sorted(set(found_files) - set(patches.filenames)), | 712 sorted(set(found_files) - set(patches.filenames)), |
731 sorted(set(patches.filenames) - set(found_files)))) | 713 sorted(set(patches.filenames) - set(found_files)))) |
732 | 714 |
733 def commit(self, commit_message, user): | 715 def commit(self, commit_message, user): |
734 """Commits, updates the commit message and pushes.""" | 716 """Commits, updates the commit message and pushes.""" |
735 assert self.commit_user | 717 assert self.commit_user |
736 assert isinstance(commit_message, unicode) | 718 assert isinstance(commit_message, unicode) |
737 current_branch = self._check_output_git( | 719 current_branch = self._check_output_git( |
(...skipping 81 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
819 self.checkout = checkout | 801 self.checkout = checkout |
820 self.post_processors = (post_processors or []) + ( | 802 self.post_processors = (post_processors or []) + ( |
821 self.checkout.post_processors or []) | 803 self.checkout.post_processors or []) |
822 | 804 |
823 def prepare(self, revision): | 805 def prepare(self, revision): |
824 return self.checkout.prepare(revision) | 806 return self.checkout.prepare(revision) |
825 | 807 |
826 def get_settings(self, key): | 808 def get_settings(self, key): |
827 return self.checkout.get_settings(key) | 809 return self.checkout.get_settings(key) |
828 | 810 |
829 def apply_patch(self, patches, post_processors=None, verbose=False, | 811 def apply_patch(self, patches, post_processors=None, verbose=False): |
830 name=None, email=None): | |
831 return self.checkout.apply_patch( | 812 return self.checkout.apply_patch( |
832 patches, post_processors or self.post_processors, verbose) | 813 patches, post_processors or self.post_processors, verbose) |
833 | 814 |
834 def commit(self, message, user): # pylint: disable=R0201 | 815 def commit(self, message, user): # pylint: disable=R0201 |
835 logging.info('Would have committed for %s with message: %s' % ( | 816 logging.info('Would have committed for %s with message: %s' % ( |
836 user, message)) | 817 user, message)) |
837 return 'FAKE' | 818 return 'FAKE' |
838 | 819 |
839 def revisions(self, rev1, rev2): | 820 def revisions(self, rev1, rev2): |
840 return self.checkout.revisions(rev1, rev2) | 821 return self.checkout.revisions(rev1, rev2) |
841 | 822 |
842 @property | 823 @property |
843 def project_name(self): | 824 def project_name(self): |
844 return self.checkout.project_name | 825 return self.checkout.project_name |
845 | 826 |
846 @property | 827 @property |
847 def project_path(self): | 828 def project_path(self): |
848 return self.checkout.project_path | 829 return self.checkout.project_path |
OLD | NEW |