OLD | NEW |
1 # Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 # Copyright (c) 2012 The Chromium Authors. All rights reserved. |
2 # Use of this source code is governed by a BSD-style license that can be | 2 # Use of this source code is governed by a BSD-style license that can be |
3 # found in the LICENSE file. | 3 # found in the LICENSE file. |
4 | 4 |
5 """SCM-specific utility classes.""" | 5 """SCM-specific utility classes.""" |
6 | 6 |
7 import cStringIO | 7 import cStringIO |
8 import glob | 8 import glob |
9 import logging | 9 import logging |
10 import os | 10 import os |
(...skipping 754 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
765 The value of the property, which will be the empty string if the property | 765 The value of the property, which will be the empty string if the property |
766 is not set on the file. If the file is not under version control, the | 766 is not set on the file. If the file is not under version control, the |
767 empty string is also returned. | 767 empty string is also returned. |
768 """ | 768 """ |
769 try: | 769 try: |
770 return SVN.Capture(['propget', property_name, filename], cwd) | 770 return SVN.Capture(['propget', property_name, filename], cwd) |
771 except subprocess2.CalledProcessError: | 771 except subprocess2.CalledProcessError: |
772 return '' | 772 return '' |
773 | 773 |
774 @staticmethod | 774 @staticmethod |
775 def DiffItem(filename, cwd, full_move, revision): | |
776 """Diffs a single file. | |
777 | |
778 Should be simple, eh? No it isn't. | |
779 Be sure to be in the appropriate directory before calling to have the | |
780 expected relative path. | |
781 full_move means that move or copy operations should completely recreate the | |
782 files, usually in the prospect to apply the patch for a try job.""" | |
783 # If the user specified a custom diff command in their svn config file, | |
784 # then it'll be used when we do svn diff, which we don't want to happen | |
785 # since we want the unified diff. Using --diff-cmd=diff doesn't always | |
786 # work, since they can have another diff executable in their path that | |
787 # gives different line endings. So we use a bogus temp directory as the | |
788 # config directory, which gets around these problems. | |
789 bogus_dir = tempfile.mkdtemp() | |
790 try: | |
791 # Use "svn info" output instead of os.path.isdir because the latter fails | |
792 # when the file is deleted. | |
793 return SVN._DiffItemInternal( | |
794 filename, | |
795 cwd, | |
796 SVN.CaptureLocalInfo([filename], cwd), | |
797 bogus_dir, | |
798 full_move, | |
799 revision) | |
800 finally: | |
801 gclient_utils.RemoveDirectory(bogus_dir) | |
802 | |
803 @staticmethod | |
804 def _DiffItemInternal(filename, cwd, info, bogus_dir, full_move, revision): | |
805 """Grabs the diff data.""" | |
806 command = ["diff", "--config-dir", bogus_dir, filename] | |
807 if revision: | |
808 command.extend(['--revision', revision]) | |
809 data = None | |
810 if SVN.IsMovedInfo(info): | |
811 if full_move: | |
812 if info.get("Node Kind") == "directory": | |
813 # Things become tricky here. It's a directory copy/move. We need to | |
814 # diff all the files inside it. | |
815 # This will put a lot of pressure on the heap. This is why StringIO | |
816 # is used and converted back into a string at the end. The reason to | |
817 # return a string instead of a StringIO is that StringIO.write() | |
818 # doesn't accept a StringIO object. *sigh*. | |
819 for (dirpath, dirnames, filenames) in os.walk(filename): | |
820 # Cleanup all files starting with a '.'. | |
821 for d in dirnames: | |
822 if d.startswith('.'): | |
823 dirnames.remove(d) | |
824 for f in filenames: | |
825 if f.startswith('.'): | |
826 filenames.remove(f) | |
827 for f in filenames: | |
828 if data is None: | |
829 data = cStringIO.StringIO() | |
830 data.write(GenFakeDiff(os.path.join(dirpath, f))) | |
831 if data: | |
832 tmp = data.getvalue() | |
833 data.close() | |
834 data = tmp | |
835 else: | |
836 data = GenFakeDiff(filename) | |
837 else: | |
838 if info.get("Node Kind") != "directory": | |
839 # svn diff on a mv/cp'd file outputs nothing if there was no change. | |
840 data = SVN.Capture(command, cwd) | |
841 if not data: | |
842 # We put in an empty Index entry so upload.py knows about them. | |
843 data = "Index: %s\n" % filename.replace(os.sep, '/') | |
844 # Otherwise silently ignore directories. | |
845 else: | |
846 if info.get("Node Kind") != "directory": | |
847 # Normal simple case. | |
848 try: | |
849 data = SVN.Capture(command, cwd) | |
850 except subprocess2.CalledProcessError: | |
851 if revision: | |
852 data = GenFakeDiff(filename) | |
853 else: | |
854 raise | |
855 # Otherwise silently ignore directories. | |
856 return data | |
857 | |
858 @staticmethod | |
859 def GenerateDiff(filenames, cwd, full_move, revision): | 775 def GenerateDiff(filenames, cwd, full_move, revision): |
860 """Returns a string containing the diff for the given file list. | 776 """Returns a string containing the diff for the given file list. |
861 | 777 |
862 The files in the list should either be absolute paths or relative to the | 778 The files in the list should either be absolute paths or relative to the |
863 given root. If no root directory is provided, the repository root will be | 779 given root. If no root directory is provided, the repository root will be |
864 used. | 780 used. |
865 The diff will always use relative paths. | 781 The diff will always use relative paths. |
866 """ | 782 """ |
867 assert isinstance(filenames, (list, tuple)) | 783 assert isinstance(filenames, (list, tuple)) |
868 root = os.path.normcase(os.path.join(cwd, '')) | 784 root = os.path.normcase(os.path.join(cwd, '')) |
(...skipping 67 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
936 buf = cStringIO.StringIO() | 852 buf = cStringIO.StringIO() |
937 for d in filter(None, diffs): | 853 for d in filter(None, diffs): |
938 buf.write(d) | 854 buf.write(d) |
939 result = buf.getvalue() | 855 result = buf.getvalue() |
940 buf.close() | 856 buf.close() |
941 return result | 857 return result |
942 finally: | 858 finally: |
943 gclient_utils.RemoveDirectory(bogus_dir) | 859 gclient_utils.RemoveDirectory(bogus_dir) |
944 | 860 |
945 @staticmethod | 861 @staticmethod |
| 862 def _DiffItemInternal(filename, cwd, info, bogus_dir, full_move, revision): |
| 863 """Grabs the diff data.""" |
| 864 command = ["diff", "--config-dir", bogus_dir, filename] |
| 865 if revision: |
| 866 command.extend(['--revision', revision]) |
| 867 data = None |
| 868 if SVN.IsMovedInfo(info): |
| 869 if full_move: |
| 870 if info.get("Node Kind") == "directory": |
| 871 # Things become tricky here. It's a directory copy/move. We need to |
| 872 # diff all the files inside it. |
| 873 # This will put a lot of pressure on the heap. This is why StringIO |
| 874 # is used and converted back into a string at the end. The reason to |
| 875 # return a string instead of a StringIO is that StringIO.write() |
| 876 # doesn't accept a StringIO object. *sigh*. |
| 877 for (dirpath, dirnames, filenames) in os.walk(filename): |
| 878 # Cleanup all files starting with a '.'. |
| 879 for d in dirnames: |
| 880 if d.startswith('.'): |
| 881 dirnames.remove(d) |
| 882 for f in filenames: |
| 883 if f.startswith('.'): |
| 884 filenames.remove(f) |
| 885 for f in filenames: |
| 886 if data is None: |
| 887 data = cStringIO.StringIO() |
| 888 data.write(GenFakeDiff(os.path.join(dirpath, f))) |
| 889 if data: |
| 890 tmp = data.getvalue() |
| 891 data.close() |
| 892 data = tmp |
| 893 else: |
| 894 data = GenFakeDiff(filename) |
| 895 else: |
| 896 if info.get("Node Kind") != "directory": |
| 897 # svn diff on a mv/cp'd file outputs nothing if there was no change. |
| 898 data = SVN.Capture(command, cwd) |
| 899 if not data: |
| 900 # We put in an empty Index entry so upload.py knows about them. |
| 901 data = "Index: %s\n" % filename.replace(os.sep, '/') |
| 902 # Otherwise silently ignore directories. |
| 903 else: |
| 904 if info.get("Node Kind") != "directory": |
| 905 # Normal simple case. |
| 906 try: |
| 907 data = SVN.Capture(command, cwd) |
| 908 except subprocess2.CalledProcessError: |
| 909 if revision: |
| 910 data = GenFakeDiff(filename) |
| 911 else: |
| 912 raise |
| 913 # Otherwise silently ignore directories. |
| 914 return data |
| 915 |
| 916 @staticmethod |
946 def GetEmail(cwd): | 917 def GetEmail(cwd): |
947 """Retrieves the svn account which we assume is an email address.""" | 918 """Retrieves the svn account which we assume is an email address.""" |
948 try: | 919 try: |
949 infos = SVN.CaptureLocalInfo([], cwd) | 920 infos = SVN.CaptureLocalInfo([], cwd) |
950 except subprocess2.CalledProcessError: | 921 except subprocess2.CalledProcessError: |
951 return None | 922 return None |
952 | 923 |
953 # Should check for uuid but it is incorrectly saved for https creds. | 924 # Should check for uuid but it is incorrectly saved for https creds. |
954 root = infos['Repository Root'] | 925 root = infos['Repository Root'] |
955 realm = root.rsplit('/', 1)[0] | 926 realm = root.rsplit('/', 1)[0] |
(...skipping 140 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1096 # revert, like for properties. | 1067 # revert, like for properties. |
1097 if not os.path.isdir(cwd): | 1068 if not os.path.isdir(cwd): |
1098 # '.' was deleted. It's not worth continuing. | 1069 # '.' was deleted. It's not worth continuing. |
1099 return | 1070 return |
1100 try: | 1071 try: |
1101 SVN.Capture(['revert', file_status[1]], cwd=cwd) | 1072 SVN.Capture(['revert', file_status[1]], cwd=cwd) |
1102 except subprocess2.CalledProcessError: | 1073 except subprocess2.CalledProcessError: |
1103 if not os.path.exists(file_path): | 1074 if not os.path.exists(file_path): |
1104 continue | 1075 continue |
1105 raise | 1076 raise |
OLD | NEW |