Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1231)

Side by Side Diff: scm.py

Issue 14050007: Second take at handling users with locally overridden diff-cmds. (Closed) Base URL: http://src.chromium.org/svn/trunk/tools/depot_tools/
Patch Set: Created 7 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « no previous file | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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
11 import re 11 import re
12 import sys 12 import sys
13 import tempfile
14 import time 13 import time
15 from xml.etree import ElementTree 14 from xml.etree import ElementTree
16 15
17 import gclient_utils 16 import gclient_utils
18 import subprocess2 17 import subprocess2
19 18
20 19
21 def ValidateEmail(email): 20 def ValidateEmail(email):
22 return (re.match(r"^[a-zA-Z0-9._%-+]+@[a-zA-Z0-9._%-]+.[a-zA-Z]{2,6}$", email) 21 return (re.match(r"^[a-zA-Z0-9._%-+]+@[a-zA-Z0-9._%-]+.[a-zA-Z]{2,6}$", email)
23 is not None) 22 is not None)
(...skipping 749 matching lines...) Expand 10 before | Expand all | Expand 10 after
773 772
774 @staticmethod 773 @staticmethod
775 def DiffItem(filename, cwd, full_move, revision): 774 def DiffItem(filename, cwd, full_move, revision):
776 """Diffs a single file. 775 """Diffs a single file.
777 776
778 Should be simple, eh? No it isn't. 777 Should be simple, eh? No it isn't.
779 Be sure to be in the appropriate directory before calling to have the 778 Be sure to be in the appropriate directory before calling to have the
780 expected relative path. 779 expected relative path.
781 full_move means that move or copy operations should completely recreate the 780 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.""" 781 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, 782 # Use "svn info" output instead of os.path.isdir because the latter fails
784 # then it'll be used when we do svn diff, which we don't want to happen 783 # when the file is deleted.
785 # since we want the unified diff. Using --diff-cmd=diff doesn't always 784 return SVN._DiffItemInternal(
786 # work, since they can have another diff executable in their path that 785 filename,
787 # gives different line endings. So we use a bogus temp directory as the 786 cwd,
788 # config directory, which gets around these problems. 787 SVN.CaptureLocalInfo([filename], cwd),
789 bogus_dir = tempfile.mkdtemp() 788 full_move,
790 try: 789 revision)
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 790
803 @staticmethod 791 @staticmethod
804 def _DiffItemInternal(filename, cwd, info, bogus_dir, full_move, revision): 792 def _DiffItemInternal(filename, cwd, info, full_move, revision):
805 """Grabs the diff data.""" 793 """Grabs the diff data."""
806 command = ["diff", "--config-dir", bogus_dir, filename] 794 command = ["diff", "--internal-diff", filename]
807 if revision: 795 if revision:
808 command.extend(['--revision', revision]) 796 command.extend(['--revision', revision])
809 data = None 797 data = None
810 if SVN.IsMovedInfo(info): 798 if SVN.IsMovedInfo(info):
811 if full_move: 799 if full_move:
812 if info.get("Node Kind") == "directory": 800 if info.get("Node Kind") == "directory":
813 # Things become tricky here. It's a directory copy/move. We need to 801 # Things become tricky here. It's a directory copy/move. We need to
814 # diff all the files inside it. 802 # diff all the files inside it.
815 # This will put a lot of pressure on the heap. This is why StringIO 803 # 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 804 # is used and converted back into a string at the end. The reason to
(...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after
864 used. 852 used.
865 The diff will always use relative paths. 853 The diff will always use relative paths.
866 """ 854 """
867 assert isinstance(filenames, (list, tuple)) 855 assert isinstance(filenames, (list, tuple))
868 root = os.path.normcase(os.path.join(cwd, '')) 856 root = os.path.normcase(os.path.join(cwd, ''))
869 def RelativePath(path, root): 857 def RelativePath(path, root):
870 """We must use relative paths.""" 858 """We must use relative paths."""
871 if os.path.normcase(path).startswith(root): 859 if os.path.normcase(path).startswith(root):
872 return path[len(root):] 860 return path[len(root):]
873 return path 861 return path
874 # If the user specified a custom diff command in their svn config file, 862 # Cleanup filenames
875 # then it'll be used when we do svn diff, which we don't want to happen 863 filenames = [RelativePath(f, root) for f in filenames]
876 # since we want the unified diff. Using --diff-cmd=diff doesn't always 864 # Get information about the modified items (files and directories)
877 # work, since they can have another diff executable in their path that 865 data = dict([(f, SVN.CaptureLocalInfo([f], root)) for f in filenames])
878 # gives different line endings. So we use a bogus temp directory as the 866 diffs = []
879 # config directory, which gets around these problems. 867 if full_move:
880 bogus_dir = tempfile.mkdtemp() 868 # Eliminate modified files inside moved/copied directory.
881 try: 869 for (filename, info) in data.iteritems():
882 # Cleanup filenames 870 if SVN.IsMovedInfo(info) and info.get("Node Kind") == "directory":
883 filenames = [RelativePath(f, root) for f in filenames] 871 # Remove files inside the directory.
884 # Get information about the modified items (files and directories) 872 filenames = [f for f in filenames
885 data = dict([(f, SVN.CaptureLocalInfo([f], root)) for f in filenames]) 873 if not f.startswith(filename + os.path.sep)]
886 diffs = [] 874 for filename in data.keys():
887 if full_move: 875 if not filename in filenames:
888 # Eliminate modified files inside moved/copied directory. 876 # Remove filtered out items.
889 for (filename, info) in data.iteritems(): 877 del data[filename]
890 if SVN.IsMovedInfo(info) and info.get("Node Kind") == "directory": 878 else:
891 # Remove files inside the directory. 879 metaheaders = []
892 filenames = [f for f in filenames 880 for (filename, info) in data.iteritems():
893 if not f.startswith(filename + os.path.sep)] 881 if SVN.IsMovedInfo(info):
894 for filename in data.keys(): 882 # for now, the most common case is a head copy,
895 if not filename in filenames: 883 # so let's just encode that as a straight up cp.
896 # Remove filtered out items. 884 srcurl = info.get('Copied From URL')
897 del data[filename] 885 file_root = info.get('Repository Root')
898 else: 886 rev = int(info.get('Copied From Rev'))
899 metaheaders = [] 887 assert srcurl.startswith(file_root)
900 for (filename, info) in data.iteritems(): 888 src = srcurl[len(file_root)+1:]
901 if SVN.IsMovedInfo(info): 889 try:
902 # for now, the most common case is a head copy, 890 srcinfo = SVN.CaptureRemoteInfo(srcurl)
903 # so let's just encode that as a straight up cp. 891 except subprocess2.CalledProcessError, e:
904 srcurl = info.get('Copied From URL') 892 if not 'Not a valid URL' in e.stderr:
905 file_root = info.get('Repository Root') 893 raise
906 rev = int(info.get('Copied From Rev')) 894 # Assume the file was deleted. No idea how to figure out at which
907 assert srcurl.startswith(file_root) 895 # revision the file was deleted.
908 src = srcurl[len(file_root)+1:] 896 srcinfo = {'Revision': rev}
909 try: 897 if (srcinfo.get('Revision') != rev and
910 srcinfo = SVN.CaptureRemoteInfo(srcurl) 898 SVN.Capture(['diff', '--internal-diff', '-r', '%d:head' % rev,
911 except subprocess2.CalledProcessError, e: 899 srcurl], cwd)):
912 if not 'Not a valid URL' in e.stderr: 900 metaheaders.append("#$ svn cp -r %d %s %s "
913 raise 901 "### WARNING: note non-trunk copy\n" %
914 # Assume the file was deleted. No idea how to figure out at which 902 (rev, src, filename))
915 # revision the file was deleted. 903 else:
916 srcinfo = {'Revision': rev} 904 metaheaders.append("#$ cp %s %s\n" % (src, filename))
917 if (srcinfo.get('Revision') != rev and
918 SVN.Capture(['diff', '-r', '%d:head' % rev, srcurl], cwd)):
919 metaheaders.append("#$ svn cp -r %d %s %s "
920 "### WARNING: note non-trunk copy\n" %
921 (rev, src, filename))
922 else:
923 metaheaders.append("#$ cp %s %s\n" % (src,
924 filename))
925 905
926 if metaheaders: 906 if metaheaders:
927 diffs.append("### BEGIN SVN COPY METADATA\n") 907 diffs.append("### BEGIN SVN COPY METADATA\n")
928 diffs.extend(metaheaders) 908 diffs.extend(metaheaders)
929 diffs.append("### END SVN COPY METADATA\n") 909 diffs.append("### END SVN COPY METADATA\n")
930 # Now ready to do the actual diff. 910 # Now ready to do the actual diff.
931 for filename in sorted(data.iterkeys()): 911 for filename in sorted(data.iterkeys()):
932 diffs.append(SVN._DiffItemInternal( 912 diffs.append(SVN._DiffItemInternal(filename, cwd, data[filename],
933 filename, cwd, data[filename], bogus_dir, full_move, revision)) 913 full_move, revision))
934 # Use StringIO since it can be messy when diffing a directory move with 914 # Use StringIO since it can be messy when diffing a directory move with
935 # full_move=True. 915 # full_move=True.
936 buf = cStringIO.StringIO() 916 buf = cStringIO.StringIO()
937 for d in filter(None, diffs): 917 for d in filter(None, diffs):
938 buf.write(d) 918 buf.write(d)
939 result = buf.getvalue() 919 result = buf.getvalue()
940 buf.close() 920 buf.close()
941 return result 921 return result
942 finally:
943 gclient_utils.RemoveDirectory(bogus_dir)
944 922
945 @staticmethod 923 @staticmethod
946 def GetEmail(cwd): 924 def GetEmail(cwd):
947 """Retrieves the svn account which we assume is an email address.""" 925 """Retrieves the svn account which we assume is an email address."""
948 try: 926 try:
949 infos = SVN.CaptureLocalInfo([], cwd) 927 infos = SVN.CaptureLocalInfo([], cwd)
950 except subprocess2.CalledProcessError: 928 except subprocess2.CalledProcessError:
951 return None 929 return None
952 930
953 # Should check for uuid but it is incorrectly saved for https creds. 931 # Should check for uuid but it is incorrectly saved for https creds.
(...skipping 142 matching lines...) Expand 10 before | Expand all | Expand 10 after
1096 # revert, like for properties. 1074 # revert, like for properties.
1097 if not os.path.isdir(cwd): 1075 if not os.path.isdir(cwd):
1098 # '.' was deleted. It's not worth continuing. 1076 # '.' was deleted. It's not worth continuing.
1099 return 1077 return
1100 try: 1078 try:
1101 SVN.Capture(['revert', file_status[1]], cwd=cwd) 1079 SVN.Capture(['revert', file_status[1]], cwd=cwd)
1102 except subprocess2.CalledProcessError: 1080 except subprocess2.CalledProcessError:
1103 if not os.path.exists(file_path): 1081 if not os.path.exists(file_path):
1104 continue 1082 continue
1105 raise 1083 raise
OLDNEW
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698