OLD | NEW |
1 # Copyright (c) 2010 The Chromium Authors. All rights reserved. | 1 # Copyright (c) 2010 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 shutil | 12 import shutil |
13 import subprocess | 13 import subprocess |
14 import sys | 14 import sys |
15 import tempfile | 15 import tempfile |
16 import time | 16 import time |
17 import xml.dom.minidom | 17 import xml.dom.minidom |
18 | 18 |
19 import gclient_utils | 19 import gclient_utils |
| 20 import subprocess2 |
| 21 |
20 | 22 |
21 def ValidateEmail(email): | 23 def ValidateEmail(email): |
22 return (re.match(r"^[a-zA-Z0-9._%-+]+@[a-zA-Z0-9._%-]+.[a-zA-Z]{2,6}$", email) | 24 return (re.match(r"^[a-zA-Z0-9._%-+]+@[a-zA-Z0-9._%-]+.[a-zA-Z]{2,6}$", email) |
23 is not None) | 25 is not None) |
24 | 26 |
25 | 27 |
26 def GetCasedPath(path): | 28 def GetCasedPath(path): |
27 """Elcheapos way to get the real path case on Windows.""" | 29 """Elcheapos way to get the real path case on Windows.""" |
28 if sys.platform.startswith('win') and os.path.exists(path): | 30 if sys.platform.startswith('win') and os.path.exists(path): |
29 # Reconstruct the path. | 31 # Reconstruct the path. |
(...skipping 233 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
263 def FetchUpstreamTuple(cwd): | 265 def FetchUpstreamTuple(cwd): |
264 """Returns a tuple containg remote and remote ref, | 266 """Returns a tuple containg remote and remote ref, |
265 e.g. 'origin', 'refs/heads/master' | 267 e.g. 'origin', 'refs/heads/master' |
266 Tries to be intelligent and understand git-svn. | 268 Tries to be intelligent and understand git-svn. |
267 """ | 269 """ |
268 remote = '.' | 270 remote = '.' |
269 branch = GIT.GetBranch(cwd) | 271 branch = GIT.GetBranch(cwd) |
270 try: | 272 try: |
271 upstream_branch = GIT.Capture( | 273 upstream_branch = GIT.Capture( |
272 ['config', 'branch.%s.merge' % branch], cwd=cwd).strip() | 274 ['config', 'branch.%s.merge' % branch], cwd=cwd).strip() |
273 except gclient_utils.Error: | 275 except (gclient_utils.Error, subprocess2.CalledProcessError): |
274 upstream_branch = None | 276 upstream_branch = None |
275 if upstream_branch: | 277 if upstream_branch: |
276 try: | 278 try: |
277 remote = GIT.Capture( | 279 remote = GIT.Capture( |
278 ['config', 'branch.%s.remote' % branch], cwd=cwd).strip() | 280 ['config', 'branch.%s.remote' % branch], cwd=cwd).strip() |
279 except gclient_utils.Error: | 281 except (gclient_utils.Error, subprocess2.CalledProcessError): |
280 pass | 282 pass |
281 else: | 283 else: |
282 try: | 284 try: |
283 upstream_branch = GIT.Capture( | 285 upstream_branch = GIT.Capture( |
284 ['config', 'rietveld.upstream-branch'], cwd=cwd).strip() | 286 ['config', 'rietveld.upstream-branch'], cwd=cwd).strip() |
285 except gclient_utils.Error: | 287 except (gclient_utils.Error, subprocess2.CalledProcessError): |
286 upstream_branch = None | 288 upstream_branch = None |
287 if upstream_branch: | 289 if upstream_branch: |
288 try: | 290 try: |
289 remote = GIT.Capture( | 291 remote = GIT.Capture( |
290 ['config', 'rietveld.upstream-remote'], cwd=cwd).strip() | 292 ['config', 'rietveld.upstream-remote'], cwd=cwd).strip() |
291 except gclient_utils.Error: | 293 except (gclient_utils.Error, subprocess2.CalledProcessError): |
292 pass | 294 pass |
293 else: | 295 else: |
294 # Fall back on trying a git-svn upstream branch. | 296 # Fall back on trying a git-svn upstream branch. |
295 if GIT.IsGitSvn(cwd): | 297 if GIT.IsGitSvn(cwd): |
296 upstream_branch = GIT.GetSVNBranch(cwd) | 298 upstream_branch = GIT.GetSVNBranch(cwd) |
297 else: | 299 else: |
298 # Else, try to guess the origin remote. | 300 # Else, try to guess the origin remote. |
299 remote_branches = GIT.Capture(['branch', '-r'], cwd=cwd).split() | 301 remote_branches = GIT.Capture(['branch', '-r'], cwd=cwd).split() |
300 if 'origin/master' in remote_branches: | 302 if 'origin/master' in remote_branches: |
301 # Fall back on origin/master if it exits. | 303 # Fall back on origin/master if it exits. |
(...skipping 150 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
452 if line.startswith('svn: '): | 454 if line.startswith('svn: '): |
453 failure.append(line) | 455 failure.append(line) |
454 | 456 |
455 try: | 457 try: |
456 gclient_utils.CheckCallAndFilterAndHeader( | 458 gclient_utils.CheckCallAndFilterAndHeader( |
457 ['svn'] + args, | 459 ['svn'] + args, |
458 cwd=cwd, | 460 cwd=cwd, |
459 always=verbose, | 461 always=verbose, |
460 filter_fn=CaptureMatchingLines, | 462 filter_fn=CaptureMatchingLines, |
461 stdout=stdout) | 463 stdout=stdout) |
462 except gclient_utils.Error: | 464 except (gclient_utils.Error, subprocess2.CalledProcessError): |
463 def IsKnownFailure(): | 465 def IsKnownFailure(): |
464 for x in failure: | 466 for x in failure: |
465 if (x.startswith('svn: OPTIONS of') or | 467 if (x.startswith('svn: OPTIONS of') or |
466 x.startswith('svn: PROPFIND of') or | 468 x.startswith('svn: PROPFIND of') or |
467 x.startswith('svn: REPORT of') or | 469 x.startswith('svn: REPORT of') or |
468 x.startswith('svn: Unknown hostname') or | 470 x.startswith('svn: Unknown hostname') or |
469 x.startswith('svn: Server sent unexpected return value')): | 471 x.startswith('svn: Server sent unexpected return value')): |
470 return True | 472 return True |
471 return False | 473 return False |
472 | 474 |
(...skipping 179 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
652 filename: The file to check | 654 filename: The file to check |
653 property_name: The name of the SVN property, e.g. "svn:mime-type" | 655 property_name: The name of the SVN property, e.g. "svn:mime-type" |
654 | 656 |
655 Returns: | 657 Returns: |
656 The value of the property, which will be the empty string if the property | 658 The value of the property, which will be the empty string if the property |
657 is not set on the file. If the file is not under version control, the | 659 is not set on the file. If the file is not under version control, the |
658 empty string is also returned. | 660 empty string is also returned. |
659 """ | 661 """ |
660 try: | 662 try: |
661 return SVN.Capture(['propget', property_name, filename]) | 663 return SVN.Capture(['propget', property_name, filename]) |
662 except gclient_utils.Error: | 664 except (gclient_utils.Error, subprocess2.CalledProcessError): |
663 return '' | 665 return '' |
664 | 666 |
665 @staticmethod | 667 @staticmethod |
666 def DiffItem(filename, full_move=False, revision=None): | 668 def DiffItem(filename, full_move=False, revision=None): |
667 """Diffs a single file. | 669 """Diffs a single file. |
668 | 670 |
669 Should be simple, eh? No it isn't. | 671 Should be simple, eh? No it isn't. |
670 Be sure to be in the appropriate directory before calling to have the | 672 Be sure to be in the appropriate directory before calling to have the |
671 expected relative path. | 673 expected relative path. |
672 full_move means that move or copy operations should completely recreate the | 674 full_move means that move or copy operations should completely recreate the |
(...skipping 160 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
833 return result | 835 return result |
834 finally: | 836 finally: |
835 os.chdir(previous_cwd) | 837 os.chdir(previous_cwd) |
836 shutil.rmtree(bogus_dir) | 838 shutil.rmtree(bogus_dir) |
837 | 839 |
838 @staticmethod | 840 @staticmethod |
839 def GetEmail(repo_root): | 841 def GetEmail(repo_root): |
840 """Retrieves the svn account which we assume is an email address.""" | 842 """Retrieves the svn account which we assume is an email address.""" |
841 try: | 843 try: |
842 infos = SVN.CaptureInfo(repo_root) | 844 infos = SVN.CaptureInfo(repo_root) |
843 except gclient_utils.Error: | 845 except (gclient_utils.Error, subprocess2.CalledProcessError): |
844 return None | 846 return None |
845 | 847 |
846 # Should check for uuid but it is incorrectly saved for https creds. | 848 # Should check for uuid but it is incorrectly saved for https creds. |
847 root = infos['Repository Root'] | 849 root = infos['Repository Root'] |
848 realm = root.rsplit('/', 1)[0] | 850 realm = root.rsplit('/', 1)[0] |
849 uuid = infos['UUID'] | 851 uuid = infos['UUID'] |
850 if root.startswith('https') or not uuid: | 852 if root.startswith('https') or not uuid: |
851 regexp = re.compile(r'<%s:\d+>.*' % realm) | 853 regexp = re.compile(r'<%s:\d+>.*' % realm) |
852 else: | 854 else: |
853 regexp = re.compile(r'<%s:\d+> %s' % (realm, uuid)) | 855 regexp = re.compile(r'<%s:\d+> %s' % (realm, uuid)) |
(...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
895 def GetCheckoutRoot(directory): | 897 def GetCheckoutRoot(directory): |
896 """Returns the top level directory of the current repository. | 898 """Returns the top level directory of the current repository. |
897 | 899 |
898 The directory is returned as an absolute path. | 900 The directory is returned as an absolute path. |
899 """ | 901 """ |
900 directory = os.path.abspath(directory) | 902 directory = os.path.abspath(directory) |
901 try: | 903 try: |
902 info = SVN.CaptureInfo(directory) | 904 info = SVN.CaptureInfo(directory) |
903 cur_dir_repo_root = info['Repository Root'] | 905 cur_dir_repo_root = info['Repository Root'] |
904 url = info['URL'] | 906 url = info['URL'] |
905 except gclient_utils.Error: | 907 except (gclient_utils.Error, subprocess2.CalledProcessError): |
906 return None | 908 return None |
907 while True: | 909 while True: |
908 parent = os.path.dirname(directory) | 910 parent = os.path.dirname(directory) |
909 try: | 911 try: |
910 info = SVN.CaptureInfo(parent) | 912 info = SVN.CaptureInfo(parent) |
911 if (info['Repository Root'] != cur_dir_repo_root or | 913 if (info['Repository Root'] != cur_dir_repo_root or |
912 info['URL'] != os.path.dirname(url)): | 914 info['URL'] != os.path.dirname(url)): |
913 break | 915 break |
914 url = info['URL'] | 916 url = info['URL'] |
915 except gclient_utils.Error: | 917 except (gclient_utils.Error, subprocess2.CalledProcessError): |
916 break | 918 break |
917 directory = parent | 919 directory = parent |
918 return GetCasedPath(directory) | 920 return GetCasedPath(directory) |
919 | 921 |
920 @staticmethod | 922 @staticmethod |
921 def AssertVersion(min_version): | 923 def AssertVersion(min_version): |
922 """Asserts svn's version is at least min_version.""" | 924 """Asserts svn's version is at least min_version.""" |
923 def only_int(val): | 925 def only_int(val): |
924 if val.isdigit(): | 926 if val.isdigit(): |
925 return int(val) | 927 return int(val) |
(...skipping 51 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
977 if (file_status[0][0] in ('D', 'A', '!') or | 979 if (file_status[0][0] in ('D', 'A', '!') or |
978 not file_status[0][1:].isspace()): | 980 not file_status[0][1:].isspace()): |
979 # Added, deleted file requires manual intervention and require calling | 981 # Added, deleted file requires manual intervention and require calling |
980 # revert, like for properties. | 982 # revert, like for properties. |
981 try: | 983 try: |
982 SVN.Capture(['revert', file_status[1]], cwd=repo_root) | 984 SVN.Capture(['revert', file_status[1]], cwd=repo_root) |
983 except gclient_utils.CheckCallError: | 985 except gclient_utils.CheckCallError: |
984 if not os.path.exists(file_path): | 986 if not os.path.exists(file_path): |
985 continue | 987 continue |
986 raise | 988 raise |
OLD | NEW |