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

Side by Side Diff: git_cl.py

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