| 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 |