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