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

Side by Side Diff: git_cl.py

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