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

Side by Side Diff: git_cl.py

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