Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(326)

Side by Side Diff: git_cl.py

Issue 1844523002: Reland of git cl: Rework Changelist class for Rietveld/Gerrit use. (Closed) Base URL: https://chromium.googlesource.com/chromium/tools/depot_tools.git@master
Patch Set: Created 4 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « no previous file | tests/git_cl_test.py » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 #!/usr/bin/env python 1 #!/usr/bin/env python
2 # Copyright (c) 2012 The Chromium Authors. All rights reserved. 2 # Copyright (c) 2012 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 # Copyright (C) 2008 Evan Martin <martine@danga.com> 6 # Copyright (C) 2008 Evan Martin <martine@danga.com>
7 7
8 """A git-command for integrating reviews on Rietveld.""" 8 """A git-command for integrating reviews on Rietveld and Gerrit."""
9 9
10 from distutils.version import LooseVersion 10 from distutils.version import LooseVersion
11 from multiprocessing.pool import ThreadPool 11 from multiprocessing.pool import ThreadPool
12 import base64 12 import base64
13 import collections 13 import collections
14 import glob 14 import glob
15 import httplib 15 import httplib
16 import json 16 import json
17 import logging 17 import logging
18 import optparse 18 import optparse
(...skipping 21 matching lines...) Expand all
40 from third_party import colorama 40 from third_party import colorama
41 from third_party import httplib2 41 from third_party import httplib2
42 from third_party import upload 42 from third_party import upload
43 import auth 43 import auth
44 from luci_hacks import trigger_luci_job as luci_trigger 44 from luci_hacks import trigger_luci_job as luci_trigger
45 import clang_format 45 import clang_format
46 import commit_queue 46 import commit_queue
47 import dart_format 47 import dart_format
48 import fix_encoding 48 import fix_encoding
49 import gclient_utils 49 import gclient_utils
50 import gerrit_util
50 import git_cache 51 import git_cache
51 import git_common 52 import git_common
52 import git_footers 53 import git_footers
53 import owners 54 import owners
54 import owners_finder 55 import owners_finder
55 import presubmit_support 56 import presubmit_support
56 import rietveld 57 import rietveld
57 import scm 58 import scm
58 import subcommand 59 import subcommand
59 import subprocess2 60 import subprocess2
(...skipping 89 matching lines...) Expand 10 before | Expand all | Expand 10 after
149 150
150 def ask_for_data(prompt): 151 def ask_for_data(prompt):
151 try: 152 try:
152 return raw_input(prompt) 153 return raw_input(prompt)
153 except KeyboardInterrupt: 154 except KeyboardInterrupt:
154 # Hide the exception. 155 # Hide the exception.
155 sys.exit(1) 156 sys.exit(1)
156 157
157 158
158 def git_set_branch_value(key, value): 159 def git_set_branch_value(key, value):
159 branch = Changelist().GetBranch() 160 branch = GetCurrentBranch()
160 if not branch: 161 if not branch:
161 return 162 return
162 163
163 cmd = ['config'] 164 cmd = ['config']
164 if isinstance(value, int): 165 if isinstance(value, int):
165 cmd.append('--int') 166 cmd.append('--int')
166 git_key = 'branch.%s.%s' % (branch, key) 167 git_key = 'branch.%s.%s' % (branch, key)
167 RunGit(cmd + [git_key, str(value)]) 168 RunGit(cmd + [git_key, str(value)])
168 169
169 170
170 def git_get_branch_default(key, default): 171 def git_get_branch_default(key, default):
171 branch = Changelist().GetBranch() 172 branch = GetCurrentBranch()
172 if branch: 173 if branch:
173 git_key = 'branch.%s.%s' % (branch, key) 174 git_key = 'branch.%s.%s' % (branch, key)
174 (_, stdout) = RunGitWithCode(['config', '--int', '--get', git_key]) 175 (_, stdout) = RunGitWithCode(['config', '--int', '--get', git_key])
175 try: 176 try:
176 return int(stdout.strip()) 177 return int(stdout.strip())
177 except ValueError: 178 except ValueError:
178 pass 179 pass
179 return default 180 return default
180 181
181 182
(...skipping 624 matching lines...) Expand 10 before | Expand all | Expand 10 after
806 def _GetBranchConfig(self, branch_name, param, **kwargs): 807 def _GetBranchConfig(self, branch_name, param, **kwargs):
807 return self._GetConfig('branch.' + branch_name + '.' + param, **kwargs) 808 return self._GetConfig('branch.' + branch_name + '.' + param, **kwargs)
808 809
809 def _GetConfig(self, param, **kwargs): 810 def _GetConfig(self, param, **kwargs):
810 self.LazyUpdateIfNeeded() 811 self.LazyUpdateIfNeeded()
811 return RunGit(['config', param], **kwargs).strip() 812 return RunGit(['config', param], **kwargs).strip()
812 813
813 814
814 def ShortBranchName(branch): 815 def ShortBranchName(branch):
815 """Convert a name like 'refs/heads/foo' to just 'foo'.""" 816 """Convert a name like 'refs/heads/foo' to just 'foo'."""
816 return branch.replace('refs/heads/', '') 817 return branch.replace('refs/heads/', '', 1)
818
819
820 def GetCurrentBranchRef():
821 """Returns branch ref (e.g., refs/heads/master) or None."""
822 return RunGit(['symbolic-ref', 'HEAD'],
823 stderr=subprocess2.VOID, error_ok=True).strip() or None
824
825
826 def GetCurrentBranch():
827 """Returns current branch or None.
828
829 For refs/heads/* branches, returns just last part. For others, full ref.
830 """
831 branchref = GetCurrentBranchRef()
832 if branchref:
833 return ShortBranchName(branchref)
834 return None
817 835
818 836
819 class Changelist(object): 837 class Changelist(object):
820 def __init__(self, branchref=None, issue=None, auth_config=None): 838 """Changelist works with one changelist in local branch.
839
840 Supports two codereview backends: Rietveld or Gerrit, selected at object
841 creation.
842
843 Not safe for concurrent multi-{thread,process} use.
844 """
845
846 def __init__(self, branchref=None, issue=None, codereview=None, **kwargs):
847 """Create a new ChangeList instance.
848
849 If issue is given, the codereview must be given too.
850
851 If `codereview` is given, it must be 'rietveld' or 'gerrit'.
852 Otherwise, it's decided based on current configuration of the local branch,
853 with default being 'rietveld' for backwards compatibility.
854 See _load_codereview_impl for more details.
855
856 **kwargs will be passed directly to codereview implementation.
857 """
821 # Poke settings so we get the "configure your server" message if necessary. 858 # Poke settings so we get the "configure your server" message if necessary.
822 global settings 859 global settings
823 if not settings: 860 if not settings:
824 # Happens when git_cl.py is used as a utility library. 861 # Happens when git_cl.py is used as a utility library.
825 settings = Settings() 862 settings = Settings()
826 settings.GetDefaultServerUrl() 863
864 if issue:
865 assert codereview, 'codereview must be known, if issue is known'
866
827 self.branchref = branchref 867 self.branchref = branchref
828 if self.branchref: 868 if self.branchref:
829 self.branch = ShortBranchName(self.branchref) 869 self.branch = ShortBranchName(self.branchref)
830 else: 870 else:
831 self.branch = None 871 self.branch = None
832 self.rietveld_server = None
833 self.upstream_branch = None 872 self.upstream_branch = None
834 self.lookedup_issue = False 873 self.lookedup_issue = False
835 self.issue = issue or None 874 self.issue = issue or None
836 self.has_description = False 875 self.has_description = False
837 self.description = None 876 self.description = None
838 self.lookedup_patchset = False 877 self.lookedup_patchset = False
839 self.patchset = None 878 self.patchset = None
840 self.cc = None 879 self.cc = None
841 self.watchers = () 880 self.watchers = ()
842 self._auth_config = auth_config
843 self._props = None
844 self._remote = None 881 self._remote = None
845 self._rpc_server = None
846 882
847 @property 883 self._codereview_impl = None
848 def auth_config(self): 884 self._load_codereview_impl(codereview, **kwargs)
849 return self._auth_config 885
886 def _load_codereview_impl(self, codereview=None, **kwargs):
887 if codereview:
888 codereview = codereview.lower()
889 if codereview == 'gerrit':
890 self._codereview_impl = _GerritChangelistImpl(self, **kwargs)
891 elif codereview == 'rietveld':
892 self._codereview_impl = _RietveldChangelistImpl(self, **kwargs)
893 else:
894 assert codereview in ('rietveld', 'gerrit')
895 return
896
897 # Automatic selection based on issue number set for a current branch.
898 # Rietveld takes precedence over Gerrit.
899 assert not self.issue
900 # Whether we find issue or not, we are doing the lookup.
901 self.lookedup_issue = True
902 for cls in [_RietveldChangelistImpl, _GerritChangelistImpl]:
903 setting = cls.IssueSetting(self.GetBranch())
904 issue = RunGit(['config', setting], error_ok=True).strip()
905 if issue:
906 self._codereview_impl = cls(self, **kwargs)
907 self.issue = int(issue)
908 return
909
910 # No issue is set for this branch, so decide based on repo-wide settings.
911 return self._load_codereview_impl(
912 codereview='gerrit' if settings.GetIsGerrit() else 'rietveld')
913
850 914
851 def GetCCList(self): 915 def GetCCList(self):
852 """Return the users cc'd on this CL. 916 """Return the users cc'd on this CL.
853 917
854 Return is a string suitable for passing to gcl with the --cc flag. 918 Return is a string suitable for passing to gcl with the --cc flag.
855 """ 919 """
856 if self.cc is None: 920 if self.cc is None:
857 base_cc = settings.GetDefaultCCList() 921 base_cc = settings.GetDefaultCCList()
858 more_cc = ','.join(self.watchers) 922 more_cc = ','.join(self.watchers)
859 self.cc = ','.join(filter(None, (base_cc, more_cc))) or '' 923 self.cc = ','.join(filter(None, (base_cc, more_cc))) or ''
860 return self.cc 924 return self.cc
861 925
862 def GetCCListWithoutDefault(self): 926 def GetCCListWithoutDefault(self):
863 """Return the users cc'd on this CL excluding default ones.""" 927 """Return the users cc'd on this CL excluding default ones."""
864 if self.cc is None: 928 if self.cc is None:
865 self.cc = ','.join(self.watchers) 929 self.cc = ','.join(self.watchers)
866 return self.cc 930 return self.cc
867 931
868 def SetWatchers(self, watchers): 932 def SetWatchers(self, watchers):
869 """Set the list of email addresses that should be cc'd based on the changed 933 """Set the list of email addresses that should be cc'd based on the changed
870 files in this CL. 934 files in this CL.
871 """ 935 """
872 self.watchers = watchers 936 self.watchers = watchers
873 937
874 def GetBranch(self): 938 def GetBranch(self):
875 """Returns the short branch name, e.g. 'master'.""" 939 """Returns the short branch name, e.g. 'master'."""
876 if not self.branch: 940 if not self.branch:
877 branchref = RunGit(['symbolic-ref', 'HEAD'], 941 branchref = GetCurrentBranchRef()
878 stderr=subprocess2.VOID, error_ok=True).strip()
879 if not branchref: 942 if not branchref:
880 return None 943 return None
881 self.branchref = branchref 944 self.branchref = branchref
882 self.branch = ShortBranchName(self.branchref) 945 self.branch = ShortBranchName(self.branchref)
883 return self.branch 946 return self.branch
884 947
885 def GetBranchRef(self): 948 def GetBranchRef(self):
886 """Returns the full branch name, e.g. 'refs/heads/master'.""" 949 """Returns the full branch name, e.g. 'refs/heads/master'."""
887 self.GetBranch() # Poke the lazy loader. 950 self.GetBranch() # Poke the lazy loader.
888 return self.branchref 951 return self.branchref
(...skipping 23 matching lines...) Expand all
912 if 'origin/master' in remote_branches: 975 if 'origin/master' in remote_branches:
913 # Fall back on origin/master if it exits. 976 # Fall back on origin/master if it exits.
914 remote = 'origin' 977 remote = 'origin'
915 upstream_branch = 'refs/heads/master' 978 upstream_branch = 'refs/heads/master'
916 elif 'origin/trunk' in remote_branches: 979 elif 'origin/trunk' in remote_branches:
917 # Fall back on origin/trunk if it exists. Generally a shared 980 # Fall back on origin/trunk if it exists. Generally a shared
918 # git-svn clone 981 # git-svn clone
919 remote = 'origin' 982 remote = 'origin'
920 upstream_branch = 'refs/heads/trunk' 983 upstream_branch = 'refs/heads/trunk'
921 else: 984 else:
922 DieWithError("""Unable to determine default branch to diff against. 985 DieWithError(
923 Either pass complete "git diff"-style arguments, like 986 'Unable to determine default branch to diff against.\n'
924 git cl upload origin/master 987 'Either pass complete "git diff"-style arguments, like\n'
925 or verify this branch is set up to track another (via the --track argument to 988 ' git cl upload origin/master\n'
926 "git checkout -b ...").""") 989 'or verify this branch is set up to track another \n'
990 '(via the --track argument to "git checkout -b ...").')
927 991
928 return remote, upstream_branch 992 return remote, upstream_branch
929 993
930 def GetCommonAncestorWithUpstream(self): 994 def GetCommonAncestorWithUpstream(self):
931 upstream_branch = self.GetUpstreamBranch() 995 upstream_branch = self.GetUpstreamBranch()
932 if not BranchExists(upstream_branch): 996 if not BranchExists(upstream_branch):
933 DieWithError('The upstream for the current branch (%s) does not exist ' 997 DieWithError('The upstream for the current branch (%s) does not exist '
934 'anymore.\nPlease fix it and try again.' % self.GetBranch()) 998 'anymore.\nPlease fix it and try again.' % self.GetBranch())
935 return git_common.get_or_create_merge_base(self.GetBranch(), 999 return git_common.get_or_create_merge_base(self.GetBranch(),
936 upstream_branch) 1000 upstream_branch)
(...skipping 121 matching lines...) Expand 10 before | Expand all | Expand 10 after
1058 # If URL is pointing to a local directory, it is probably a git cache. 1122 # If URL is pointing to a local directory, it is probably a git cache.
1059 if os.path.isdir(url): 1123 if os.path.isdir(url):
1060 url = RunGit(['config', 'remote.%s.url' % remote], 1124 url = RunGit(['config', 'remote.%s.url' % remote],
1061 error_ok=True, 1125 error_ok=True,
1062 cwd=url).strip() 1126 cwd=url).strip()
1063 return url 1127 return url
1064 1128
1065 def GetIssue(self): 1129 def GetIssue(self):
1066 """Returns the issue number as a int or None if not set.""" 1130 """Returns the issue number as a int or None if not set."""
1067 if self.issue is None and not self.lookedup_issue: 1131 if self.issue is None and not self.lookedup_issue:
1068 issue = RunGit(['config', self._IssueSetting()], error_ok=True).strip() 1132 issue = RunGit(['config',
1133 self._codereview_impl.IssueSetting(self.GetBranch())],
1134 error_ok=True).strip()
1069 self.issue = int(issue) or None if issue else None 1135 self.issue = int(issue) or None if issue else None
1070 self.lookedup_issue = True 1136 self.lookedup_issue = True
1071 return self.issue 1137 return self.issue
1072 1138
1073 def GetRietveldServer(self):
1074 if not self.rietveld_server:
1075 # If we're on a branch then get the server potentially associated
1076 # with that branch.
1077 if self.GetIssue():
1078 rietveld_server_config = self._RietveldServer()
1079 if rietveld_server_config:
1080 self.rietveld_server = gclient_utils.UpgradeToHttps(RunGit(
1081 ['config', rietveld_server_config], error_ok=True).strip())
1082 if not self.rietveld_server:
1083 self.rietveld_server = settings.GetDefaultServerUrl()
1084 return self.rietveld_server
1085
1086 def GetGerritServer(self):
1087 # We don't support multiple Gerrit servers, and assume it to be same as
1088 # origin, except with a '-review' suffix for first subdomain.
1089 parts = urlparse.urlparse(self.GetRemoteUrl()).netloc.split('.')
1090 parts[0] = parts[0] + '-review'
1091 return 'https://%s' % '.'.join(parts)
1092
1093 def GetIssueURL(self): 1139 def GetIssueURL(self):
1094 """Get the URL for a particular issue.""" 1140 """Get the URL for a particular issue."""
1095 if not self.GetIssue(): 1141 issue = self.GetIssue()
1142 if not issue:
1096 return None 1143 return None
1097 if settings.GetIsGerrit(): 1144 return '%s/%s' % (self._codereview_impl.GetCodereviewServer(), issue)
1098 return '%s/%s' % (self.GetGerritServer(), self.GetIssue())
1099 return '%s/%s' % (self.GetRietveldServer(), self.GetIssue())
1100 1145
1101 def GetDescription(self, pretty=False): 1146 def GetDescription(self, pretty=False):
1102 if not self.has_description: 1147 if not self.has_description:
1103 if self.GetIssue(): 1148 if self.GetIssue():
1104 issue = self.GetIssue() 1149 self.description = self._codereview_impl.FetchDescription()
1105 try:
1106 self.description = self.RpcServer().get_description(issue).strip()
1107 except urllib2.HTTPError as e:
1108 if e.code == 404:
1109 DieWithError(
1110 ('\nWhile fetching the description for issue %d, received a '
1111 '404 (not found)\n'
1112 'error. It is likely that you deleted this '
1113 'issue on the server. If this is the\n'
1114 'case, please run\n\n'
1115 ' git cl issue 0\n\n'
1116 'to clear the association with the deleted issue. Then run '
1117 'this command again.') % issue)
1118 else:
1119 DieWithError(
1120 '\nFailed to fetch issue description. HTTP error %d' % e.code)
1121 except urllib2.URLError as e:
1122 print >> sys.stderr, (
1123 'Warning: Failed to retrieve CL description due to network '
1124 'failure.')
1125 self.description = ''
1126
1127 self.has_description = True 1150 self.has_description = True
1128 if pretty: 1151 if pretty:
1129 wrapper = textwrap.TextWrapper() 1152 wrapper = textwrap.TextWrapper()
1130 wrapper.initial_indent = wrapper.subsequent_indent = ' ' 1153 wrapper.initial_indent = wrapper.subsequent_indent = ' '
1131 return wrapper.fill(self.description) 1154 return wrapper.fill(self.description)
1132 return self.description 1155 return self.description
1133 1156
1134 def GetPatchset(self): 1157 def GetPatchset(self):
1135 """Returns the patchset number as a int or None if not set.""" 1158 """Returns the patchset number as a int or None if not set."""
1136 if self.patchset is None and not self.lookedup_patchset: 1159 if self.patchset is None and not self.lookedup_patchset:
1137 patchset = RunGit(['config', self._PatchsetSetting()], 1160 patchset = RunGit(['config', self._codereview_impl.PatchsetSetting()],
1138 error_ok=True).strip() 1161 error_ok=True).strip()
1139 self.patchset = int(patchset) or None if patchset else None 1162 self.patchset = int(patchset) or None if patchset else None
1140 self.lookedup_patchset = True 1163 self.lookedup_patchset = True
1141 return self.patchset 1164 return self.patchset
1142 1165
1143 def SetPatchset(self, patchset): 1166 def SetPatchset(self, patchset):
1144 """Set this branch's patchset. If patchset=0, clears the patchset.""" 1167 """Set this branch's patchset. If patchset=0, clears the patchset."""
1168 patchset_setting = self._codereview_impl.PatchsetSetting()
1145 if patchset: 1169 if patchset:
1146 RunGit(['config', self._PatchsetSetting(), str(patchset)]) 1170 RunGit(['config', patchset_setting, str(patchset)])
1147 self.patchset = patchset 1171 self.patchset = patchset
1148 else: 1172 else:
1149 RunGit(['config', '--unset', self._PatchsetSetting()], 1173 RunGit(['config', '--unset', patchset_setting],
1150 stderr=subprocess2.PIPE, error_ok=True) 1174 stderr=subprocess2.PIPE, error_ok=True)
1151 self.patchset = None 1175 self.patchset = None
1152 1176
1153 def GetMostRecentPatchset(self):
1154 return self.GetIssueProperties()['patchsets'][-1]
1155
1156 def GetPatchSetDiff(self, issue, patchset):
1157 return self.RpcServer().get(
1158 '/download/issue%s_%s.diff' % (issue, patchset))
1159
1160 def GetIssueProperties(self):
1161 if self._props is None:
1162 issue = self.GetIssue()
1163 if not issue:
1164 self._props = {}
1165 else:
1166 self._props = self.RpcServer().get_issue_properties(issue, True)
1167 return self._props
1168
1169 def GetApprovingReviewers(self):
1170 return get_approving_reviewers(self.GetIssueProperties())
1171
1172 def AddComment(self, message):
1173 return self.RpcServer().add_comment(self.GetIssue(), message)
1174
1175 def SetIssue(self, issue=None): 1177 def SetIssue(self, issue=None):
1176 """Set this branch's issue. If issue isn't given, clears the issue.""" 1178 """Set this branch's issue. If issue isn't given, clears the issue."""
1179 issue_setting = self._codereview_impl.IssueSetting(self.GetBranch())
1180 codereview_setting = self._codereview_impl.GetCodereviewServerSetting()
1177 if issue: 1181 if issue:
1178 self.issue = issue 1182 self.issue = issue
1179 RunGit(['config', self._IssueSetting(), str(issue)]) 1183 RunGit(['config', issue_setting, str(issue)])
1180 if not settings.GetIsGerrit() and self.rietveld_server: 1184 codereview_server = self._codereview_impl.GetCodereviewServer()
1181 RunGit(['config', self._RietveldServer(), self.rietveld_server]) 1185 if codereview_server:
1186 RunGit(['config', codereview_setting, codereview_server])
1182 else: 1187 else:
1183 current_issue = self.GetIssue() 1188 current_issue = self.GetIssue()
1184 if current_issue: 1189 if current_issue:
1185 RunGit(['config', '--unset', self._IssueSetting()]) 1190 RunGit(['config', '--unset', issue_setting])
1186 self.issue = None 1191 self.issue = None
1187 self.SetPatchset(None) 1192 self.SetPatchset(None)
1188 1193
1189 def GetChange(self, upstream_branch, author): 1194 def GetChange(self, upstream_branch, author):
1190 if not self.GitSanityChecks(upstream_branch): 1195 if not self.GitSanityChecks(upstream_branch):
1191 DieWithError('\nGit sanity check failure') 1196 DieWithError('\nGit sanity check failure')
1192 1197
1193 root = settings.GetRelativeRoot() 1198 root = settings.GetRelativeRoot()
1194 if not root: 1199 if not root:
1195 root = '.' 1200 root = '.'
(...skipping 29 matching lines...) Expand all
1225 return presubmit_support.GitChange( 1230 return presubmit_support.GitChange(
1226 name, 1231 name,
1227 description, 1232 description,
1228 absroot, 1233 absroot,
1229 files, 1234 files,
1230 issue, 1235 issue,
1231 patchset, 1236 patchset,
1232 author, 1237 author,
1233 upstream=upstream_branch) 1238 upstream=upstream_branch)
1234 1239
1240 def UpdateDescription(self, description):
1241 self.description = description
1242 return self._codereview_impl.UpdateDescriptionRemote(description)
1243
1244 def RunHook(self, committing, may_prompt, verbose, change):
1245 """Calls sys.exit() if the hook fails; returns a HookResults otherwise."""
1246 try:
1247 return presubmit_support.DoPresubmitChecks(change, committing,
1248 verbose=verbose, output_stream=sys.stdout, input_stream=sys.stdin,
1249 default_presubmit=None, may_prompt=may_prompt,
1250 rietveld_obj=self._codereview_impl.GetRieveldObjForPresubmit())
1251 except presubmit_support.PresubmitFailure, e:
1252 DieWithError(
1253 ('%s\nMaybe your depot_tools is out of date?\n'
1254 'If all fails, contact maruel@') % e)
1255
1256 # Forward methods to codereview specific implementation.
1257
1258 def CloseIssue(self):
1259 return self._codereview_impl.CloseIssue()
1260
1261 def GetStatus(self):
1262 return self._codereview_impl.GetStatus()
1263
1264 def GetCodereviewServer(self):
1265 return self._codereview_impl.GetCodereviewServer()
1266
1267 def GetApprovingReviewers(self):
1268 return self._codereview_impl.GetApprovingReviewers()
1269
1270 def GetMostRecentPatchset(self):
1271 return self._codereview_impl.GetMostRecentPatchset()
1272
1273 def __getattr__(self, attr):
1274 # This is because lots of untested code accesses Rietveld-specific stuff
1275 # directly, and it's hard to fix for sure. So, just let it work, and fix
1276 # on a cases by case basis.
1277 return getattr(self._codereview_impl, attr)
1278
1279
1280 class _ChangelistCodereviewBase(object):
1281 """Abstract base class encapsulating codereview specifics of a changelist."""
1282 def __init__(self, changelist):
1283 self._changelist = changelist # instance of Changelist
1284
1285 def __getattr__(self, attr):
1286 # Forward methods to changelist.
1287 # TODO(tandrii): maybe clean up _GerritChangelistImpl and
1288 # _RietveldChangelistImpl to avoid this hack?
1289 return getattr(self._changelist, attr)
1290
1235 def GetStatus(self): 1291 def GetStatus(self):
1236 """Apply a rough heuristic to give a simple summary of an issue's review 1292 """Apply a rough heuristic to give a simple summary of an issue's review
1237 or CQ status, assuming adherence to a common workflow. 1293 or CQ status, assuming adherence to a common workflow.
1294
1295 Returns None if no issue for this branch, or specific string keywords.
1296 """
1297 raise NotImplementedError()
1298
1299 def GetCodereviewServer(self):
1300 """Returns server URL without end slash, like "https://codereview.com"."""
1301 raise NotImplementedError()
1302
1303 def FetchDescription(self):
1304 """Fetches and returns description from the codereview server."""
1305 raise NotImplementedError()
1306
1307 def GetCodereviewServerSetting(self):
1308 """Returns git config setting for the codereview server."""
1309 raise NotImplementedError()
1310
1311 @staticmethod
1312 def IssueSetting(branch):
1313 """Returns name of git config setting which stores issue number for a given
1314 branch."""
1315 raise NotImplementedError()
1316
1317 def PatchsetSetting(self):
1318 """Returns name of git config setting which stores issue number."""
1319 raise NotImplementedError()
1320
1321 def GetRieveldObjForPresubmit(self):
1322 # This is an unfortunate Rietveld-embeddedness in presubmit.
1323 # For non-Rietveld codereviews, this probably should return a dummy object.
1324 raise NotImplementedError()
1325
1326 def UpdateDescriptionRemote(self, description):
1327 """Update the description on codereview site."""
1328 raise NotImplementedError()
1329
1330 def CloseIssue(self):
1331 """Closes the issue."""
1332 raise NotImplementedError()
1333
1334 def GetApprovingReviewers(self):
1335 """Returns a list of reviewers approving the change.
1336
1337 Note: not necessarily committers.
1338 """
1339 raise NotImplementedError()
1340
1341 def GetMostRecentPatchset(self):
1342 """Returns the most recent patchset number from the codereview site."""
1343 raise NotImplementedError()
1344
1345
1346 class _RietveldChangelistImpl(_ChangelistCodereviewBase):
1347 def __init__(self, changelist, auth_config=None, rietveld_server=None):
1348 super(_RietveldChangelistImpl, self).__init__(changelist)
1349 assert settings, 'must be initialized in _ChangelistCodereviewBase'
1350 settings.GetDefaultServerUrl()
1351
1352 self._rietveld_server = rietveld_server
1353 self._auth_config = auth_config
1354 self._props = None
1355 self._rpc_server = None
1356
1357 def GetAuthConfig(self):
1358 return self._auth_config
1359
1360 def GetCodereviewServer(self):
1361 if not self._rietveld_server:
1362 # If we're on a branch then get the server potentially associated
1363 # with that branch.
1364 if self.GetIssue():
1365 rietveld_server_setting = self.GetCodereviewServerSetting()
1366 if rietveld_server_setting:
1367 self._rietveld_server = gclient_utils.UpgradeToHttps(RunGit(
1368 ['config', rietveld_server_setting], error_ok=True).strip())
1369 if not self._rietveld_server:
1370 self._rietveld_server = settings.GetDefaultServerUrl()
1371 return self._rietveld_server
1372
1373 def FetchDescription(self):
1374 issue = self.GetIssue()
1375 assert issue
1376 try:
1377 return self.RpcServer().get_description(issue).strip()
1378 except urllib2.HTTPError as e:
1379 if e.code == 404:
1380 DieWithError(
1381 ('\nWhile fetching the description for issue %d, received a '
1382 '404 (not found)\n'
1383 'error. It is likely that you deleted this '
1384 'issue on the server. If this is the\n'
1385 'case, please run\n\n'
1386 ' git cl issue 0\n\n'
1387 'to clear the association with the deleted issue. Then run '
1388 'this command again.') % issue)
1389 else:
1390 DieWithError(
1391 '\nFailed to fetch issue description. HTTP error %d' % e.code)
1392 except urllib2.URLError as e:
1393 print >> sys.stderr, (
1394 'Warning: Failed to retrieve CL description due to network '
1395 'failure.')
1396 return ''
1397
1398 def GetMostRecentPatchset(self):
1399 return self.GetIssueProperties()['patchsets'][-1]
1400
1401 def GetPatchSetDiff(self, issue, patchset):
1402 return self.RpcServer().get(
1403 '/download/issue%s_%s.diff' % (issue, patchset))
1404
1405 def GetIssueProperties(self):
1406 if self._props is None:
1407 issue = self.GetIssue()
1408 if not issue:
1409 self._props = {}
1410 else:
1411 self._props = self.RpcServer().get_issue_properties(issue, True)
1412 return self._props
1413
1414 def GetApprovingReviewers(self):
1415 return get_approving_reviewers(self.GetIssueProperties())
1416
1417 def AddComment(self, message):
1418 return self.RpcServer().add_comment(self.GetIssue(), message)
1419
1420 def GetStatus(self):
1421 """Apply a rough heuristic to give a simple summary of an issue's review
1422 or CQ status, assuming adherence to a common workflow.
1238 1423
1239 Returns None if no issue for this branch, or one of the following keywords: 1424 Returns None if no issue for this branch, or one of the following keywords:
1240 * 'error' - error from review tool (including deleted issues) 1425 * 'error' - error from review tool (including deleted issues)
1241 * 'unsent' - not sent for review 1426 * 'unsent' - not sent for review
1242 * 'waiting' - waiting for review 1427 * 'waiting' - waiting for review
1243 * 'reply' - waiting for owner to reply to review 1428 * 'reply' - waiting for owner to reply to review
1244 * 'lgtm' - LGTM from at least one approved reviewer 1429 * 'lgtm' - LGTM from at least one approved reviewer
1245 * 'commit' - in the commit queue 1430 * 'commit' - in the commit queue
1246 * 'closed' - closed 1431 * 'closed' - closed
1247 """ 1432 """
(...skipping 24 matching lines...) Expand all
1272 messages = props.get('messages') or [] 1457 messages = props.get('messages') or []
1273 1458
1274 if not messages: 1459 if not messages:
1275 # No message was sent. 1460 # No message was sent.
1276 return 'unsent' 1461 return 'unsent'
1277 if messages[-1]['sender'] != props.get('owner_email'): 1462 if messages[-1]['sender'] != props.get('owner_email'):
1278 # Non-LGTM reply from non-owner 1463 # Non-LGTM reply from non-owner
1279 return 'reply' 1464 return 'reply'
1280 return 'waiting' 1465 return 'waiting'
1281 1466
1282 def RunHook(self, committing, may_prompt, verbose, change): 1467 def UpdateDescriptionRemote(self, description):
1283 """Calls sys.exit() if the hook fails; returns a HookResults otherwise."""
1284
1285 try:
1286 return presubmit_support.DoPresubmitChecks(change, committing,
1287 verbose=verbose, output_stream=sys.stdout, input_stream=sys.stdin,
1288 default_presubmit=None, may_prompt=may_prompt,
1289 rietveld_obj=self.RpcServer())
1290 except presubmit_support.PresubmitFailure, e:
1291 DieWithError(
1292 ('%s\nMaybe your depot_tools is out of date?\n'
1293 'If all fails, contact maruel@') % e)
1294
1295 def UpdateDescription(self, description):
1296 self.description = description
1297 return self.RpcServer().update_description( 1468 return self.RpcServer().update_description(
1298 self.GetIssue(), self.description) 1469 self.GetIssue(), self.description)
1299 1470
1300 def CloseIssue(self): 1471 def CloseIssue(self):
1301 """Updates the description and closes the issue."""
1302 return self.RpcServer().close_issue(self.GetIssue()) 1472 return self.RpcServer().close_issue(self.GetIssue())
1303 1473
1304 def SetFlag(self, flag, value): 1474 def SetFlag(self, flag, value):
1305 """Patchset must match.""" 1475 """Patchset must match."""
1306 if not self.GetPatchset(): 1476 if not self.GetPatchset():
1307 DieWithError('The patchset needs to match. Send another patchset.') 1477 DieWithError('The patchset needs to match. Send another patchset.')
1308 try: 1478 try:
1309 return self.RpcServer().set_flag( 1479 return self.RpcServer().set_flag(
1310 self.GetIssue(), self.GetPatchset(), flag, value) 1480 self.GetIssue(), self.GetPatchset(), flag, value)
1311 except urllib2.HTTPError, e: 1481 except urllib2.HTTPError, e:
1312 if e.code == 404: 1482 if e.code == 404:
1313 DieWithError('The issue %s doesn\'t exist.' % self.GetIssue()) 1483 DieWithError('The issue %s doesn\'t exist.' % self.GetIssue())
1314 if e.code == 403: 1484 if e.code == 403:
1315 DieWithError( 1485 DieWithError(
1316 ('Access denied to issue %s. Maybe the patchset %s doesn\'t ' 1486 ('Access denied to issue %s. Maybe the patchset %s doesn\'t '
1317 'match?') % (self.GetIssue(), self.GetPatchset())) 1487 'match?') % (self.GetIssue(), self.GetPatchset()))
1318 raise 1488 raise
1319 1489
1320 def RpcServer(self): 1490 def RpcServer(self):
1321 """Returns an upload.RpcServer() to access this review's rietveld instance. 1491 """Returns an upload.RpcServer() to access this review's rietveld instance.
1322 """ 1492 """
1323 if not self._rpc_server: 1493 if not self._rpc_server:
1324 self._rpc_server = rietveld.CachingRietveld( 1494 self._rpc_server = rietveld.CachingRietveld(
1325 self.GetRietveldServer(), 1495 self.GetCodereviewServer(),
1326 self._auth_config or auth.make_auth_config()) 1496 self._auth_config or auth.make_auth_config())
1327 return self._rpc_server 1497 return self._rpc_server
1328 1498
1329 def _IssueSetting(self): 1499 @staticmethod
1330 """Return the git setting that stores this change's issue.""" 1500 def IssueSetting(branch):
1331 return 'branch.%s.rietveldissue' % self.GetBranch() 1501 return 'branch.%s.rietveldissue' % branch
1332 1502
1333 def _PatchsetSetting(self): 1503 def PatchsetSetting(self):
1334 """Return the git setting that stores this change's most recent patchset.""" 1504 """Return the git setting that stores this change's most recent patchset."""
1335 return 'branch.%s.rietveldpatchset' % self.GetBranch() 1505 return 'branch.%s.rietveldpatchset' % self.GetBranch()
1336 1506
1337 def _RietveldServer(self): 1507 def GetCodereviewServerSetting(self):
1338 """Returns the git setting that stores this change's rietveld server.""" 1508 """Returns the git setting that stores this change's rietveld server."""
1339 branch = self.GetBranch() 1509 branch = self.GetBranch()
1340 if branch: 1510 if branch:
1341 return 'branch.%s.rietveldserver' % branch 1511 return 'branch.%s.rietveldserver' % branch
1342 return None 1512 return None
1343 1513
1514 def GetRieveldObjForPresubmit(self):
1515 return self.RpcServer()
1516
1517
1518 class _GerritChangelistImpl(_ChangelistCodereviewBase):
1519 def __init__(self, changelist, auth_config=None):
1520 # auth_config is Rietveld thing, kept here to preserve interface only.
1521 super(_GerritChangelistImpl, self).__init__(changelist)
1522 self._change_id = None
1523 self._gerrit_server = None # e.g. https://chromium-review.googlesource.com
1524 self._gerrit_host = None # e.g. chromium-review.googlesource.com
1525
1526 def _GetGerritHost(self):
1527 # Lazy load of configs.
1528 self.GetCodereviewServer()
1529 return self._gerrit_host
1530
1531 def GetCodereviewServer(self):
1532 if not self._gerrit_server:
1533 # If we're on a branch then get the server potentially associated
1534 # with that branch.
1535 if self.GetIssue():
1536 gerrit_server_setting = self.GetCodereviewServerSetting()
1537 if gerrit_server_setting:
1538 self._gerrit_server = RunGit(['config', gerrit_server_setting],
1539 error_ok=True).strip()
1540 if self._gerrit_server:
1541 self._gerrit_host = urlparse.urlparse(self._gerrit_server).netloc
1542 if not self._gerrit_server:
1543 # We assume repo to be hosted on Gerrit, and hence Gerrit server
1544 # has "-review" suffix for lowest level subdomain.
1545 parts = urlparse.urlparse(self.GetRemoteUrl()).netloc.split('.')
1546 parts[0] = parts[0] + '-review'
1547 self._gerrit_host = '.'.join(parts)
1548 self._gerrit_server = 'https://%s' % self._gerrit_host
1549 return self._gerrit_server
1550
1551 @staticmethod
1552 def IssueSetting(branch):
1553 return 'branch.%s.gerritissue' % branch
1554
1555 def PatchsetSetting(self):
1556 """Return the git setting that stores this change's most recent patchset."""
1557 return 'branch.%s.gerritpatchset' % self.GetBranch()
1558
1559 def GetCodereviewServerSetting(self):
1560 """Returns the git setting that stores this change's Gerrit server."""
1561 branch = self.GetBranch()
1562 if branch:
1563 return 'branch.%s.gerritserver' % branch
1564 return None
1565
1566 def GetRieveldObjForPresubmit(self):
1567 class ThisIsNotRietveldIssue(object):
1568 def __nonzero__(self):
1569 # This is a hack to make presubmit_support think that rietveld is not
1570 # defined, yet still ensure that calls directly result in a decent
1571 # exception message below.
1572 return False
1573
1574 def __getattr__(self, attr):
1575 print(
1576 'You aren\'t using Rietveld at the moment, but Gerrit.\n'
1577 'Using Rietveld in your PRESUBMIT scripts won\'t work.\n'
1578 'Please, either change your PRESUBIT to not use rietveld_obj.%s,\n'
1579 'or use Rietveld for codereview.\n'
1580 'See also http://crbug.com/579160.' % attr)
1581 raise NotImplementedError()
1582 return ThisIsNotRietveldIssue()
1583
1584 def GetStatus(self):
1585 # TODO(tandrii)
1586 raise NotImplementedError()
1587
1588 def GetMostRecentPatchset(self):
1589 data = gerrit_util.GetChangeDetail(self._GetGerritHost(), self.GetIssue(),
1590 ['CURRENT_REVISION'])
1591 return data['revisions'][data['current_revision']]['_number']
1592
1593 def FetchDescription(self):
1594 data = gerrit_util.GetChangeDetail(self._GetGerritHost(), self.GetIssue(),
1595 ['COMMIT_FOOTERS', 'CURRENT_REVISION'])
1596 return data['revisions'][data['current_revision']]['commit_with_footers']
1597
1598 def UpdateDescriptionRemote(self, description):
1599 # TODO(tandrii)
1600 raise NotImplementedError()
1601
1602 def CloseIssue(self):
1603 gerrit_util.AbandonChange(self._GetGerritHost(), self.GetIssue(), msg='')
1604
1344 1605
1345 class ChangeDescription(object): 1606 class ChangeDescription(object):
1346 """Contains a parsed form of the change description.""" 1607 """Contains a parsed form of the change description."""
1347 R_LINE = r'^[ \t]*(TBR|R)[ \t]*=[ \t]*(.*?)[ \t]*$' 1608 R_LINE = r'^[ \t]*(TBR|R)[ \t]*=[ \t]*(.*?)[ \t]*$'
1348 BUG_LINE = r'^[ \t]*(BUG)[ \t]*=[ \t]*(.*?)[ \t]*$' 1609 BUG_LINE = r'^[ \t]*(BUG)[ \t]*=[ \t]*(.*?)[ \t]*$'
1349 1610
1350 def __init__(self, description): 1611 def __init__(self, description):
1351 self._description_lines = (description or '').strip().splitlines() 1612 self._description_lines = (description or '').strip().splitlines()
1352 1613
1353 @property # www.logilab.org/ticket/89786 1614 @property # www.logilab.org/ticket/89786
(...skipping 525 matching lines...) Expand 10 before | Expand all | Expand 10 after
1879 return 0 2140 return 0
1880 2141
1881 branches = RunGit(['for-each-ref', '--format=%(refname)', 'refs/heads']) 2142 branches = RunGit(['for-each-ref', '--format=%(refname)', 'refs/heads'])
1882 if not branches: 2143 if not branches:
1883 print('No local branch found.') 2144 print('No local branch found.')
1884 return 0 2145 return 0
1885 2146
1886 changes = ( 2147 changes = (
1887 Changelist(branchref=b, auth_config=auth_config) 2148 Changelist(branchref=b, auth_config=auth_config)
1888 for b in branches.splitlines()) 2149 for b in branches.splitlines())
2150 # TODO(tandrii): refactor to use CLs list instead of branches list.
1889 branches = [c.GetBranch() for c in changes] 2151 branches = [c.GetBranch() for c in changes]
1890 alignment = max(5, max(len(b) for b in branches)) 2152 alignment = max(5, max(len(b) for b in branches))
1891 print 'Branches associated with reviews:' 2153 print 'Branches associated with reviews:'
1892 output = get_cl_statuses(branches, 2154 output = get_cl_statuses(branches,
1893 fine_grained=not options.fast, 2155 fine_grained=not options.fast,
1894 max_processes=options.maxjobs, 2156 max_processes=options.maxjobs,
1895 auth_config=auth_config) 2157 auth_config=auth_config)
1896 2158
1897 branch_statuses = {} 2159 branch_statuses = {}
1898 alignment = max(5, max(len(ShortBranchName(b)) for b in branches)) 2160 alignment = max(5, max(len(ShortBranchName(b)) for b in branches))
(...skipping 95 matching lines...) Expand 10 before | Expand all | Expand 10 after
1994 options, args = parser.parse_args(args) 2256 options, args = parser.parse_args(args)
1995 auth_config = auth.extract_auth_config_from_options(options) 2257 auth_config = auth.extract_auth_config_from_options(options)
1996 2258
1997 issue = None 2259 issue = None
1998 if options.issue: 2260 if options.issue:
1999 try: 2261 try:
2000 issue = int(options.issue) 2262 issue = int(options.issue)
2001 except ValueError: 2263 except ValueError:
2002 DieWithError('A review issue id is expected to be a number') 2264 DieWithError('A review issue id is expected to be a number')
2003 2265
2004 cl = Changelist(issue=issue, auth_config=auth_config) 2266 cl = Changelist(issue=issue, codereview='rietveld', auth_config=auth_config)
2005 2267
2006 if options.comment: 2268 if options.comment:
2007 cl.AddComment(options.comment) 2269 cl.AddComment(options.comment)
2008 return 0 2270 return 0
2009 2271
2010 data = cl.GetIssueProperties() 2272 data = cl.GetIssueProperties()
2011 summary = [] 2273 summary = []
2012 for message in sorted(data.get('messages', []), key=lambda x: x['date']): 2274 for message in sorted(data.get('messages', []), key=lambda x: x['date']):
2013 summary.append({ 2275 summary.append({
2014 'date': message['date'], 2276 'date': message['date'],
(...skipping 358 matching lines...) Expand 10 before | Expand all | Expand 10 after
2373 remote_branch = remote_branch.replace('refs/remotes/', 'refs/') 2635 remote_branch = remote_branch.replace('refs/remotes/', 'refs/')
2374 # If a pending prefix exists then replace refs/ with it. 2636 # If a pending prefix exists then replace refs/ with it.
2375 if pending_prefix: 2637 if pending_prefix:
2376 remote_branch = remote_branch.replace('refs/', pending_prefix) 2638 remote_branch = remote_branch.replace('refs/', pending_prefix)
2377 return remote_branch 2639 return remote_branch
2378 2640
2379 2641
2380 def RietveldUpload(options, args, cl, change): 2642 def RietveldUpload(options, args, cl, change):
2381 """upload the patch to rietveld.""" 2643 """upload the patch to rietveld."""
2382 upload_args = ['--assume_yes'] # Don't ask about untracked files. 2644 upload_args = ['--assume_yes'] # Don't ask about untracked files.
2383 upload_args.extend(['--server', cl.GetRietveldServer()]) 2645 upload_args.extend(['--server', cl.GetCodereviewServer()])
2384 upload_args.extend(auth.auth_config_to_command_options(cl.auth_config)) 2646 # TODO(tandrii): refactor this ugliness into _RietveldChangelistImpl.
2647 upload_args.extend(auth.auth_config_to_command_options(
2648 cl._codereview_impl.GetAuthConfig()))
2385 if options.emulate_svn_auto_props: 2649 if options.emulate_svn_auto_props:
2386 upload_args.append('--emulate_svn_auto_props') 2650 upload_args.append('--emulate_svn_auto_props')
2387 2651
2388 change_desc = None 2652 change_desc = None
2389 2653
2390 if options.email is not None: 2654 if options.email is not None:
2391 upload_args.extend(['--email', options.email]) 2655 upload_args.extend(['--email', options.email])
2392 2656
2393 if cl.GetIssue(): 2657 if cl.GetIssue():
2394 if options.title: 2658 if options.title:
(...skipping 222 matching lines...) Expand 10 before | Expand all | Expand 10 after
2617 2881
2618 # Default to diffing against common ancestor of upstream branch 2882 # Default to diffing against common ancestor of upstream branch
2619 base_branch = cl.GetCommonAncestorWithUpstream() 2883 base_branch = cl.GetCommonAncestorWithUpstream()
2620 args = [base_branch, 'HEAD'] 2884 args = [base_branch, 'HEAD']
2621 2885
2622 # Make sure authenticated to Rietveld before running expensive hooks. It is 2886 # Make sure authenticated to Rietveld before running expensive hooks. It is
2623 # a fast, best efforts check. Rietveld still can reject the authentication 2887 # a fast, best efforts check. Rietveld still can reject the authentication
2624 # during the actual upload. 2888 # during the actual upload.
2625 if not settings.GetIsGerrit() and auth_config.use_oauth2: 2889 if not settings.GetIsGerrit() and auth_config.use_oauth2:
2626 authenticator = auth.get_authenticator_for_host( 2890 authenticator = auth.get_authenticator_for_host(
2627 cl.GetRietveldServer(), auth_config) 2891 cl.GetCodereviewServer(), auth_config)
2628 if not authenticator.has_cached_credentials(): 2892 if not authenticator.has_cached_credentials():
2629 raise auth.LoginRequiredError(cl.GetRietveldServer()) 2893 raise auth.LoginRequiredError(cl.GetCodereviewServer())
2630 2894
2631 # Apply watchlists on upload. 2895 # Apply watchlists on upload.
2632 change = cl.GetChange(base_branch, None) 2896 change = cl.GetChange(base_branch, None)
2633 watchlist = watchlists.Watchlists(change.RepositoryRoot()) 2897 watchlist = watchlists.Watchlists(change.RepositoryRoot())
2634 files = [f.LocalPath() for f in change.AffectedFiles()] 2898 files = [f.LocalPath() for f in change.AffectedFiles()]
2635 if not options.bypass_watchlists: 2899 if not options.bypass_watchlists:
2636 cl.SetWatchers(watchlist.GetWatchersForPaths(files)) 2900 cl.SetWatchers(watchlist.GetWatchersForPaths(files))
2637 2901
2638 if not options.bypass_hooks: 2902 if not options.bypass_hooks:
2639 if options.reviewers or options.tbr_owners: 2903 if options.reviewers or options.tbr_owners:
(...skipping 573 matching lines...) Expand 10 before | Expand all | Expand 10 after
3213 return PatchIssue(issue_arg, options.reject, options.nocommit, 3477 return PatchIssue(issue_arg, options.reject, options.nocommit,
3214 options.directory, auth_config) 3478 options.directory, auth_config)
3215 3479
3216 3480
3217 def PatchIssue(issue_arg, reject, nocommit, directory, auth_config): 3481 def PatchIssue(issue_arg, reject, nocommit, directory, auth_config):
3218 # PatchIssue should never be called with a dirty tree. It is up to the 3482 # PatchIssue should never be called with a dirty tree. It is up to the
3219 # caller to check this, but just in case we assert here since the 3483 # caller to check this, but just in case we assert here since the
3220 # consequences of the caller not checking this could be dire. 3484 # consequences of the caller not checking this could be dire.
3221 assert(not git_common.is_dirty_git_tree('apply')) 3485 assert(not git_common.is_dirty_git_tree('apply'))
3222 3486
3487 # TODO(tandrii): implement for Gerrit.
3223 if type(issue_arg) is int or issue_arg.isdigit(): 3488 if type(issue_arg) is int or issue_arg.isdigit():
3224 # Input is an issue id. Figure out the URL. 3489 # Input is an issue id. Figure out the URL.
3225 issue = int(issue_arg) 3490 issue = int(issue_arg)
3226 cl = Changelist(issue=issue, auth_config=auth_config) 3491 cl = Changelist(issue=issue, codereview='rietveld', auth_config=auth_config)
3227 patchset = cl.GetMostRecentPatchset() 3492 patchset = cl.GetMostRecentPatchset()
3228 patch_data = cl.GetPatchSetDiff(issue, patchset) 3493 patch_data = cl._codereview_impl.GetPatchSetDiff(issue, patchset)
3229 else: 3494 else:
3230 # Assume it's a URL to the patch. Default to https. 3495 # Assume it's a URL to the patch. Default to https.
3231 issue_url = gclient_utils.UpgradeToHttps(issue_arg) 3496 issue_url = gclient_utils.UpgradeToHttps(issue_arg)
3232 match = re.match(r'(.*?)/download/issue(\d+)_(\d+).diff', issue_url) 3497 match = re.match(r'(.*?)/download/issue(\d+)_(\d+).diff', issue_url)
3233 if not match: 3498 if not match:
3234 DieWithError('Must pass an issue ID or full URL for ' 3499 DieWithError('Must pass an issue ID or full URL for '
3235 '\'Download raw patch set\'') 3500 '\'Download raw patch set\'')
3236 issue = int(match.group(2)) 3501 issue = int(match.group(2))
3237 cl = Changelist(issue=issue, auth_config=auth_config) 3502 cl = Changelist(issue=issue, codereview='rietveld',
3238 cl.rietveld_server = match.group(1) 3503 rietvled_server=match.group(1), auth_config=auth_config)
3239 patchset = int(match.group(3)) 3504 patchset = int(match.group(3))
3240 patch_data = urllib2.urlopen(issue_arg).read() 3505 patch_data = urllib2.urlopen(issue_arg).read()
3241 3506
3242 # Switch up to the top-level directory, if necessary, in preparation for 3507 # Switch up to the top-level directory, if necessary, in preparation for
3243 # applying the patch. 3508 # applying the patch.
3244 top = settings.GetRelativeRoot() 3509 top = settings.GetRelativeRoot()
3245 if top: 3510 if top:
3246 os.chdir(top) 3511 os.chdir(top)
3247 3512
3248 # Git patches have a/ at the beginning of source paths. We strip that out 3513 # Git patches have a/ at the beginning of source paths. We strip that out
(...skipping 23 matching lines...) Expand all
3272 except subprocess2.CalledProcessError: 3537 except subprocess2.CalledProcessError:
3273 print 'Failed to apply the patch' 3538 print 'Failed to apply the patch'
3274 return 1 3539 return 1
3275 3540
3276 # If we had an issue, commit the current state and register the issue. 3541 # If we had an issue, commit the current state and register the issue.
3277 if not nocommit: 3542 if not nocommit:
3278 RunGit(['commit', '-m', (cl.GetDescription() + '\n\n' + 3543 RunGit(['commit', '-m', (cl.GetDescription() + '\n\n' +
3279 'patch from issue %(i)s at patchset ' 3544 'patch from issue %(i)s at patchset '
3280 '%(p)s (http://crrev.com/%(i)s#ps%(p)s)' 3545 '%(p)s (http://crrev.com/%(i)s#ps%(p)s)'
3281 % {'i': issue, 'p': patchset})]) 3546 % {'i': issue, 'p': patchset})])
3282 cl = Changelist(auth_config=auth_config) 3547 cl = Changelist(codereview='rietveld', auth_config=auth_config,
3548 rietveld_server=cl.GetCodereviewServer())
3283 cl.SetIssue(issue) 3549 cl.SetIssue(issue)
3284 cl.SetPatchset(patchset) 3550 cl.SetPatchset(patchset)
3285 print "Committed patch locally." 3551 print "Committed patch locally."
3286 else: 3552 else:
3287 print "Patch applied to index." 3553 print "Patch applied to index."
3288 return 0 3554 return 0
3289 3555
3290 3556
3291 def CMDrebase(parser, args): 3557 def CMDrebase(parser, args):
3292 """Rebases current branch on top of svn repo.""" 3558 """Rebases current branch on top of svn repo."""
(...skipping 714 matching lines...) Expand 10 before | Expand all | Expand 10 after
4007 if __name__ == '__main__': 4273 if __name__ == '__main__':
4008 # These affect sys.stdout so do it outside of main() to simplify mocks in 4274 # These affect sys.stdout so do it outside of main() to simplify mocks in
4009 # unit testing. 4275 # unit testing.
4010 fix_encoding.fix_encoding() 4276 fix_encoding.fix_encoding()
4011 colorama.init() 4277 colorama.init()
4012 try: 4278 try:
4013 sys.exit(main(sys.argv[1:])) 4279 sys.exit(main(sys.argv[1:]))
4014 except KeyboardInterrupt: 4280 except KeyboardInterrupt:
4015 sys.stderr.write('interrupted\n') 4281 sys.stderr.write('interrupted\n')
4016 sys.exit(1) 4282 sys.exit(1)
OLDNEW
« no previous file with comments | « no previous file | tests/git_cl_test.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698