OLD | NEW |
1 #!/usr/bin/python | 1 #!/usr/bin/python |
2 # Copyright (c) 2006-2009 The Chromium Authors. All rights reserved. | 2 # Copyright (c) 2006-2009 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 | 5 |
6 """Wrapper script around Rietveld's upload.py that groups files into | 6 """Wrapper script around Rietveld's upload.py that groups files into |
7 changelists.""" | 7 changelists.""" |
8 | 8 |
9 import getpass | 9 import getpass |
10 import os | 10 import os |
11 import random | 11 import random |
12 import re | 12 import re |
13 import shutil | 13 import shutil |
14 import string | 14 import string |
15 import subprocess | 15 import subprocess |
16 import sys | 16 import sys |
17 import tempfile | 17 import tempfile |
18 import upload | 18 import upload |
19 import urllib2 | 19 import urllib2 |
20 | 20 |
21 # gcl now depends on gclient. | 21 # gcl now depends on gclient. |
22 import gclient_scm | 22 from scm import SVN |
23 import gclient_utils | 23 import gclient_utils |
24 | 24 |
25 __version__ = '1.1.1' | 25 __version__ = '1.1.2' |
26 | 26 |
27 | 27 |
28 CODEREVIEW_SETTINGS = { | 28 CODEREVIEW_SETTINGS = { |
29 # Default values. | 29 # Default values. |
30 "CODE_REVIEW_SERVER": "codereview.chromium.org", | 30 "CODE_REVIEW_SERVER": "codereview.chromium.org", |
31 "CC_LIST": "chromium-reviews@googlegroups.com", | 31 "CC_LIST": "chromium-reviews@googlegroups.com", |
32 "VIEW_VC": "http://src.chromium.org/viewvc/chrome?view=rev&revision=", | 32 "VIEW_VC": "http://src.chromium.org/viewvc/chrome?view=rev&revision=", |
33 } | 33 } |
34 | 34 |
35 # globals that store the root of the current repository and the directory where | 35 # globals that store the root of the current repository and the directory where |
36 # we store information about changelists. | 36 # we store information about changelists. |
37 REPOSITORY_ROOT = "" | 37 REPOSITORY_ROOT = "" |
38 | 38 |
39 # Filename where we store repository specific information for gcl. | 39 # Filename where we store repository specific information for gcl. |
40 CODEREVIEW_SETTINGS_FILE = "codereview.settings" | 40 CODEREVIEW_SETTINGS_FILE = "codereview.settings" |
41 | 41 |
42 # Warning message when the change appears to be missing tests. | 42 # Warning message when the change appears to be missing tests. |
43 MISSING_TEST_MSG = "Change contains new or modified methods, but no new tests!" | 43 MISSING_TEST_MSG = "Change contains new or modified methods, but no new tests!" |
44 | 44 |
45 # Global cache of files cached in GetCacheDir(). | 45 # Global cache of files cached in GetCacheDir(). |
46 FILES_CACHE = {} | 46 FILES_CACHE = {} |
47 | 47 |
48 | 48 |
49 ### SVN Functions | |
50 | |
51 def IsSVNMoved(filename): | |
52 """Determine if a file has been added through svn mv""" | |
53 info = gclient_scm.CaptureSVNInfo(filename) | |
54 return (info.get('Copied From URL') and | |
55 info.get('Copied From Rev') and | |
56 info.get('Schedule') == 'add') | |
57 | |
58 | |
59 def GetSVNFileProperty(file, property_name): | |
60 """Returns the value of an SVN property for the given file. | |
61 | |
62 Args: | |
63 file: The file to check | |
64 property_name: The name of the SVN property, e.g. "svn:mime-type" | |
65 | |
66 Returns: | |
67 The value of the property, which will be the empty string if the property | |
68 is not set on the file. If the file is not under version control, the | |
69 empty string is also returned. | |
70 """ | |
71 output = RunShell(["svn", "propget", property_name, file]) | |
72 if (output.startswith("svn: ") and | |
73 output.endswith("is not under version control")): | |
74 return "" | |
75 else: | |
76 return output | |
77 | |
78 | |
79 def UnknownFiles(extra_args): | 49 def UnknownFiles(extra_args): |
80 """Runs svn status and prints unknown files. | 50 """Runs svn status and returns unknown files. |
81 | 51 |
82 Any args in |extra_args| are passed to the tool to support giving alternate | 52 Any args in |extra_args| are passed to the tool to support giving alternate |
83 code locations. | 53 code locations. |
84 """ | 54 """ |
85 return [item[1] for item in gclient_scm.CaptureSVNStatus(extra_args) | 55 return [item[1] for item in SVN.CaptureStatus(extra_args) |
86 if item[0][0] == '?'] | 56 if item[0][0] == '?'] |
87 | 57 |
88 | 58 |
89 def GetRepositoryRoot(): | 59 def GetRepositoryRoot(): |
90 """Returns the top level directory of the current repository. | 60 """Returns the top level directory of the current repository. |
91 | 61 |
92 The directory is returned as an absolute path. | 62 The directory is returned as an absolute path. |
93 """ | 63 """ |
94 global REPOSITORY_ROOT | 64 global REPOSITORY_ROOT |
95 if not REPOSITORY_ROOT: | 65 if not REPOSITORY_ROOT: |
96 infos = gclient_scm.CaptureSVNInfo(os.getcwd(), print_error=False) | 66 infos = SVN.CaptureInfo(os.getcwd(), print_error=False) |
97 cur_dir_repo_root = infos.get("Repository Root") | 67 cur_dir_repo_root = infos.get("Repository Root") |
98 if not cur_dir_repo_root: | 68 if not cur_dir_repo_root: |
99 raise gclient_utils.Error("gcl run outside of repository") | 69 raise gclient_utils.Error("gcl run outside of repository") |
100 | 70 |
101 REPOSITORY_ROOT = os.getcwd() | 71 REPOSITORY_ROOT = os.getcwd() |
102 while True: | 72 while True: |
103 parent = os.path.dirname(REPOSITORY_ROOT) | 73 parent = os.path.dirname(REPOSITORY_ROOT) |
104 if (gclient_scm.CaptureSVNInfo(parent, print_error=False).get( | 74 if (SVN.CaptureInfo(parent, print_error=False).get( |
105 "Repository Root") != cur_dir_repo_root): | 75 "Repository Root") != cur_dir_repo_root): |
106 break | 76 break |
107 REPOSITORY_ROOT = parent | 77 REPOSITORY_ROOT = parent |
108 return REPOSITORY_ROOT | 78 return REPOSITORY_ROOT |
109 | 79 |
110 | 80 |
111 def GetInfoDir(): | 81 def GetInfoDir(): |
112 """Returns the directory where gcl info files are stored.""" | 82 """Returns the directory where gcl info files are stored.""" |
113 return os.path.join(GetRepositoryRoot(), '.svn', 'gcl_info') | 83 return os.path.join(GetRepositoryRoot(), '.svn', 'gcl_info') |
114 | 84 |
(...skipping 24 matching lines...) Expand all Loading... |
139 FILES_CACHE[filename] = None | 109 FILES_CACHE[filename] = None |
140 # First we check if we have a cached version. | 110 # First we check if we have a cached version. |
141 try: | 111 try: |
142 cached_file = os.path.join(GetCacheDir(), filename) | 112 cached_file = os.path.join(GetCacheDir(), filename) |
143 except gclient_utils.Error: | 113 except gclient_utils.Error: |
144 return None | 114 return None |
145 if (not os.path.exists(cached_file) or | 115 if (not os.path.exists(cached_file) or |
146 os.stat(cached_file).st_mtime > max_age): | 116 os.stat(cached_file).st_mtime > max_age): |
147 local_dir = os.path.dirname(os.path.abspath(filename)) | 117 local_dir = os.path.dirname(os.path.abspath(filename)) |
148 local_base = os.path.basename(filename) | 118 local_base = os.path.basename(filename) |
149 dir_info = gclient_scm.CaptureSVNInfo(".") | 119 dir_info = SVN.CaptureInfo(".") |
150 repo_root = dir_info["Repository Root"] | 120 repo_root = dir_info["Repository Root"] |
151 if use_root: | 121 if use_root: |
152 url_path = repo_root | 122 url_path = repo_root |
153 else: | 123 else: |
154 url_path = dir_info["URL"] | 124 url_path = dir_info["URL"] |
155 content = "" | 125 content = "" |
156 while True: | 126 while True: |
157 # First, look for a locally modified version of the file if we can. | 127 # First, look for a locally modified version of the file if we can. |
158 r = "" | 128 r = "" |
159 if not use_root: | 129 if not use_root: |
160 local_path = os.path.join(local_dir, local_base) | 130 local_path = os.path.join(local_dir, local_base) |
161 r = gclient_scm.CaptureSVNStatus((local_path,)) | 131 r = SVN.CaptureStatus((local_path,)) |
162 rc = -1 | 132 rc = -1 |
163 if r: | 133 if r: |
164 status = r[0][0] | 134 status = r[0][0] |
165 rc = 0 | 135 rc = 0 |
166 if not rc and status[0] in ('A','M'): | 136 if not rc and status[0] in ('A','M'): |
167 content = ReadFile(local_path) | 137 content = ReadFile(local_path) |
168 rc = 0 | 138 rc = 0 |
169 else: | 139 else: |
170 # Look in the repository if we didn't find something local. | 140 # Look in the repository if we didn't find something local. |
171 svn_path = url_path + "/" + filename | 141 svn_path = url_path + "/" + filename |
(...skipping 299 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
471 files = [] | 441 files = [] |
472 for line in split_data[1].splitlines(): | 442 for line in split_data[1].splitlines(): |
473 status = line[:7] | 443 status = line[:7] |
474 filename = line[7:] | 444 filename = line[7:] |
475 files.append((status, filename)) | 445 files.append((status, filename)) |
476 description = split_data[2] | 446 description = split_data[2] |
477 save = False | 447 save = False |
478 if update_status: | 448 if update_status: |
479 for item in files: | 449 for item in files: |
480 filename = os.path.join(local_root, item[1]) | 450 filename = os.path.join(local_root, item[1]) |
481 status_result = gclient_scm.CaptureSVNStatus(filename) | 451 status_result = SVN.CaptureStatus(filename) |
482 if not status_result or not status_result[0][0]: | 452 if not status_result or not status_result[0][0]: |
483 # File has been reverted. | 453 # File has been reverted. |
484 save = True | 454 save = True |
485 files.remove(item) | 455 files.remove(item) |
486 continue | 456 continue |
487 status = status_result[0][0] | 457 status = status_result[0][0] |
488 if status != item[0]: | 458 if status != item[0]: |
489 save = True | 459 save = True |
490 files[files.index(item)] = (status, item[1]) | 460 files[files.index(item)] = (status, item[1]) |
491 change_info = ChangeInfo(changename, issue, patchset, description, files, | 461 change_info = ChangeInfo(changename, issue, patchset, description, files, |
(...skipping 63 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
555 | 525 |
556 # Get a list of all files in changelists. | 526 # Get a list of all files in changelists. |
557 files_in_cl = {} | 527 files_in_cl = {} |
558 for cl in GetCLs(): | 528 for cl in GetCLs(): |
559 change_info = ChangeInfo.Load(cl, GetRepositoryRoot(), | 529 change_info = ChangeInfo.Load(cl, GetRepositoryRoot(), |
560 fail_on_not_found=True, update_status=False) | 530 fail_on_not_found=True, update_status=False) |
561 for status, filename in change_info.GetFiles(): | 531 for status, filename in change_info.GetFiles(): |
562 files_in_cl[filename] = change_info.name | 532 files_in_cl[filename] = change_info.name |
563 | 533 |
564 # Get all the modified files. | 534 # Get all the modified files. |
565 status_result = gclient_scm.CaptureSVNStatus(None) | 535 status_result = SVN.CaptureStatus(None) |
566 for line in status_result: | 536 for line in status_result: |
567 status = line[0] | 537 status = line[0] |
568 filename = line[1] | 538 filename = line[1] |
569 if status[0] == "?": | 539 if status[0] == "?": |
570 continue | 540 continue |
571 if dir_prefix: | 541 if dir_prefix: |
572 filename = os.path.join(dir_prefix, filename) | 542 filename = os.path.join(dir_prefix, filename) |
573 change_list_name = "" | 543 change_list_name = "" |
574 if filename in files_in_cl: | 544 if filename in files_in_cl: |
575 change_list_name = files_in_cl[filename] | 545 change_list_name = files_in_cl[filename] |
(...skipping 166 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
742 used. | 712 used. |
743 """ | 713 """ |
744 previous_cwd = os.getcwd() | 714 previous_cwd = os.getcwd() |
745 if root is None: | 715 if root is None: |
746 os.chdir(GetRepositoryRoot()) | 716 os.chdir(GetRepositoryRoot()) |
747 else: | 717 else: |
748 os.chdir(root) | 718 os.chdir(root) |
749 | 719 |
750 diff = [] | 720 diff = [] |
751 for filename in files: | 721 for filename in files: |
| 722 # TODO(maruel): Use SVN.DiffItem(). |
752 # Use svn info output instead of os.path.isdir because the latter fails | 723 # Use svn info output instead of os.path.isdir because the latter fails |
753 # when the file is deleted. | 724 # when the file is deleted. |
754 if gclient_scm.CaptureSVNInfo(filename).get("Node Kind") in ("dir", | 725 if SVN.CaptureInfo(filename).get('Node Kind') == 'directory': |
755 "directory"): | |
756 continue | 726 continue |
757 # If the user specified a custom diff command in their svn config file, | 727 # If the user specified a custom diff command in their svn config file, |
758 # then it'll be used when we do svn diff, which we don't want to happen | 728 # then it'll be used when we do svn diff, which we don't want to happen |
759 # since we want the unified diff. Using --diff-cmd=diff doesn't always | 729 # since we want the unified diff. Using --diff-cmd=diff doesn't always |
760 # work, since they can have another diff executable in their path that | 730 # work, since they can have another diff executable in their path that |
761 # gives different line endings. So we use a bogus temp directory as the | 731 # gives different line endings. So we use a bogus temp directory as the |
762 # config directory, which gets around these problems. | 732 # config directory, which gets around these problems. |
763 if sys.platform.startswith("win"): | 733 if sys.platform.startswith("win"): |
764 parent_dir = tempfile.gettempdir() | 734 parent_dir = tempfile.gettempdir() |
765 else: | 735 else: |
766 parent_dir = sys.path[0] # tempdir is not secure. | 736 parent_dir = sys.path[0] # tempdir is not secure. |
767 bogus_dir = os.path.join(parent_dir, "temp_svn_config") | 737 bogus_dir = os.path.join(parent_dir, "temp_svn_config") |
768 if not os.path.exists(bogus_dir): | 738 if not os.path.exists(bogus_dir): |
769 os.mkdir(bogus_dir) | 739 os.mkdir(bogus_dir) |
770 output = RunShell(["svn", "diff", "--config-dir", bogus_dir, filename]) | 740 output = RunShell(["svn", "diff", "--config-dir", bogus_dir, filename]) |
771 if output: | 741 if output: |
772 diff.append(output) | 742 diff.append(output) |
773 elif IsSVNMoved(filename): | 743 elif SVN.IsMoved(filename): |
774 # svn diff on a mv/cp'd file outputs nothing. | 744 # svn diff on a mv/cp'd file outputs nothing. |
775 # We put in an empty Index entry so upload.py knows about them. | 745 # We put in an empty Index entry so upload.py knows about them. |
776 diff.append("\nIndex: %s\n" % filename) | 746 diff.append("\nIndex: %s\n" % filename) |
777 else: | 747 else: |
778 # The file is not modified anymore. It should be removed from the set. | 748 # The file is not modified anymore. It should be removed from the set. |
779 pass | 749 pass |
780 os.chdir(previous_cwd) | 750 os.chdir(previous_cwd) |
781 return "".join(diff) | 751 return "".join(diff) |
782 | 752 |
783 | 753 |
(...skipping 205 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
989 change_info.description += "\nCommitted: " + viewvc_url + revision | 959 change_info.description += "\nCommitted: " + viewvc_url + revision |
990 change_info.CloseIssue() | 960 change_info.CloseIssue() |
991 os.chdir(previous_cwd) | 961 os.chdir(previous_cwd) |
992 | 962 |
993 | 963 |
994 def Change(change_info, args): | 964 def Change(change_info, args): |
995 """Creates/edits a changelist.""" | 965 """Creates/edits a changelist.""" |
996 silent = FilterFlag(args, "--silent") | 966 silent = FilterFlag(args, "--silent") |
997 | 967 |
998 # Verify the user is running the change command from a read-write checkout. | 968 # Verify the user is running the change command from a read-write checkout. |
999 svn_info = gclient_scm.CaptureSVNInfo('.') | 969 svn_info = SVN.CaptureInfo('.') |
1000 if not svn_info: | 970 if not svn_info: |
1001 ErrorExit("Current checkout is unversioned. Please retry with a versioned " | 971 ErrorExit("Current checkout is unversioned. Please retry with a versioned " |
1002 "directory.") | 972 "directory.") |
1003 | 973 |
1004 if (len(args) == 1): | 974 if (len(args) == 1): |
1005 filename = args[0] | 975 filename = args[0] |
1006 f = open(filename, 'rU') | 976 f = open(filename, 'rU') |
1007 override_description = f.read() | 977 override_description = f.read() |
1008 f.close() | 978 f.close() |
1009 else: | 979 else: |
1010 override_description = None | 980 override_description = None |
1011 | 981 |
1012 if change_info.issue: | 982 if change_info.issue: |
1013 try: | 983 try: |
1014 description = GetIssueDescription(change_info.issue) | 984 description = GetIssueDescription(change_info.issue) |
1015 except urllib2.HTTPError, err: | 985 except urllib2.HTTPError, err: |
1016 if err.code == 404: | 986 if err.code == 404: |
1017 # The user deleted the issue in Rietveld, so forget the old issue id. | 987 # The user deleted the issue in Rietveld, so forget the old issue id. |
1018 description = change_info.description | 988 description = change_info.description |
1019 change_info.issue = 0 | 989 change_info.issue = 0 |
1020 change_info.Save() | 990 change_info.Save() |
1021 else: | 991 else: |
(...skipping 272 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1294 return 0 | 1264 return 0 |
1295 args =["svn", command] | 1265 args =["svn", command] |
1296 root = GetRepositoryRoot() | 1266 root = GetRepositoryRoot() |
1297 args.extend([os.path.join(root, x) for x in change_info.GetFileNames()]) | 1267 args.extend([os.path.join(root, x) for x in change_info.GetFileNames()]) |
1298 RunShell(args, True) | 1268 RunShell(args, True) |
1299 return 0 | 1269 return 0 |
1300 | 1270 |
1301 | 1271 |
1302 if __name__ == "__main__": | 1272 if __name__ == "__main__": |
1303 sys.exit(main()) | 1273 sys.exit(main()) |
OLD | NEW |