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