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

Side by Side Diff: git_cl.py

Issue 1831813003: Revert of git cl: Rework Changelist class for Rietveld/Gerrit use. (Closed) Base URL: https://chromium.googlesource.com/chromium/tools/depot_tools.git@G100
Patch Set: 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 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.
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
919 850
920 def GetCCList(self): 851 def GetCCList(self):
921 """Return the users cc'd on this CL. 852 """Return the users cc'd on this CL.
922 853
923 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.
924 """ 855 """
925 if self.cc is None: 856 if self.cc is None:
926 base_cc = settings.GetDefaultCCList() 857 base_cc = settings.GetDefaultCCList()
927 more_cc = ','.join(self.watchers) 858 more_cc = ','.join(self.watchers)
928 self.cc = ','.join(filter(None, (base_cc, more_cc))) or '' 859 self.cc = ','.join(filter(None, (base_cc, more_cc))) or ''
929 return self.cc 860 return self.cc
930 861
931 def GetCCListWithoutDefault(self): 862 def GetCCListWithoutDefault(self):
932 """Return the users cc'd on this CL excluding default ones.""" 863 """Return the users cc'd on this CL excluding default ones."""
933 if self.cc is None: 864 if self.cc is None:
934 self.cc = ','.join(self.watchers) 865 self.cc = ','.join(self.watchers)
935 return self.cc 866 return self.cc
936 867
937 def SetWatchers(self, watchers): 868 def SetWatchers(self, watchers):
938 """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
939 files in this CL. 870 files in this CL.
940 """ 871 """
941 self.watchers = watchers 872 self.watchers = watchers
942 873
943 def GetBranch(self): 874 def GetBranch(self):
944 """Returns the short branch name, e.g. 'master'.""" 875 """Returns the short branch name, e.g. 'master'."""
945 if not self.branch: 876 if not self.branch:
946 branchref = GetCurrentBranchRef() 877 branchref = RunGit(['symbolic-ref', 'HEAD'],
878 stderr=subprocess2.VOID, error_ok=True).strip()
947 if not branchref: 879 if not branchref:
948 return None 880 return None
949 self.branchref = branchref 881 self.branchref = branchref
950 self.branch = ShortBranchName(self.branchref) 882 self.branch = ShortBranchName(self.branchref)
951 return self.branch 883 return self.branch
952 884
953 def GetBranchRef(self): 885 def GetBranchRef(self):
954 """Returns the full branch name, e.g. 'refs/heads/master'.""" 886 """Returns the full branch name, e.g. 'refs/heads/master'."""
955 self.GetBranch() # Poke the lazy loader. 887 self.GetBranch() # Poke the lazy loader.
956 return self.branchref 888 return self.branchref
(...skipping 23 matching lines...) Expand all
980 if 'origin/master' in remote_branches: 912 if 'origin/master' in remote_branches:
981 # Fall back on origin/master if it exits. 913 # Fall back on origin/master if it exits.
982 remote = 'origin' 914 remote = 'origin'
983 upstream_branch = 'refs/heads/master' 915 upstream_branch = 'refs/heads/master'
984 elif 'origin/trunk' in remote_branches: 916 elif 'origin/trunk' in remote_branches:
985 # Fall back on origin/trunk if it exists. Generally a shared 917 # Fall back on origin/trunk if it exists. Generally a shared
986 # git-svn clone 918 # git-svn clone
987 remote = 'origin' 919 remote = 'origin'
988 upstream_branch = 'refs/heads/trunk' 920 upstream_branch = 'refs/heads/trunk'
989 else: 921 else:
990 DieWithError( 922 DieWithError("""Unable to determine default branch to diff against.
991 'Unable to determine default branch to diff against.\n' 923 Either pass complete "git diff"-style arguments, like
992 'Either pass complete "git diff"-style arguments, like\n' 924 git cl upload origin/master
993 ' git cl upload origin/master\n' 925 or verify this branch is set up to track another (via the --track argument to
994 'or verify this branch is set up to track another \n' 926 "git checkout -b ...").""")
995 '(via the --track argument to "git checkout -b ...").')
996 927
997 return remote, upstream_branch 928 return remote, upstream_branch
998 929
999 def GetCommonAncestorWithUpstream(self): 930 def GetCommonAncestorWithUpstream(self):
1000 upstream_branch = self.GetUpstreamBranch() 931 upstream_branch = self.GetUpstreamBranch()
1001 if not BranchExists(upstream_branch): 932 if not BranchExists(upstream_branch):
1002 DieWithError('The upstream for the current branch (%s) does not exist ' 933 DieWithError('The upstream for the current branch (%s) does not exist '
1003 'anymore.\nPlease fix it and try again.' % self.GetBranch()) 934 'anymore.\nPlease fix it and try again.' % self.GetBranch())
1004 return git_common.get_or_create_merge_base(self.GetBranch(), 935 return git_common.get_or_create_merge_base(self.GetBranch(),
1005 upstream_branch) 936 upstream_branch)
(...skipping 118 matching lines...) Expand 10 before | Expand all | Expand 10 after
1124 remote, _ = self.GetRemoteBranch() 1055 remote, _ = self.GetRemoteBranch()
1125 url = RunGit(['config', 'remote.%s.url' % remote], error_ok=True).strip() 1056 url = RunGit(['config', 'remote.%s.url' % remote], error_ok=True).strip()
1126 1057
1127 # 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.
1128 if os.path.isdir(url): 1059 if os.path.isdir(url):
1129 url = RunGit(['config', 'remote.%s.url' % remote], 1060 url = RunGit(['config', 'remote.%s.url' % remote],
1130 error_ok=True, 1061 error_ok=True,
1131 cwd=url).strip() 1062 cwd=url).strip()
1132 return url 1063 return url
1133 1064
1134 def GetIssue(self, force_lookup=False): 1065 def GetIssue(self):
1135 """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."""
1136 if force_lookup or (self.issue is None and not self.lookedup_issue): 1067 if self.issue is None and not self.lookedup_issue:
1137 issue = RunGit(['config', self._codereview_impl.IssueSetting()], 1068 issue = RunGit(['config', self._IssueSetting()], error_ok=True).strip()
1138 error_ok=True).strip()
1139 self.issue = int(issue) or None if issue else None 1069 self.issue = int(issue) or None if issue else None
1140 self.lookedup_issue = True 1070 self.lookedup_issue = True
1141 return self.issue 1071 return self.issue
1142 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
1143 def GetIssueURL(self): 1093 def GetIssueURL(self):
1144 """Get the URL for a particular issue.""" 1094 """Get the URL for a particular issue."""
1145 issue = self.GetIssue() 1095 if not self.GetIssue():
1146 if not issue:
1147 return None 1096 return None
1148 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())
1149 1100
1150 def GetDescription(self, pretty=False): 1101 def GetDescription(self, pretty=False):
1151 if not self.has_description: 1102 if not self.has_description:
1152 if self.GetIssue(): 1103 if self.GetIssue():
1153 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
1154 self.has_description = True 1127 self.has_description = True
1155 if pretty: 1128 if pretty:
1156 wrapper = textwrap.TextWrapper() 1129 wrapper = textwrap.TextWrapper()
1157 wrapper.initial_indent = wrapper.subsequent_indent = ' ' 1130 wrapper.initial_indent = wrapper.subsequent_indent = ' '
1158 return wrapper.fill(self.description) 1131 return wrapper.fill(self.description)
1159 return self.description 1132 return self.description
1160 1133
1161 def GetPatchset(self): 1134 def GetPatchset(self):
1162 """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."""
1163 if self.patchset is None and not self.lookedup_patchset: 1136 if self.patchset is None and not self.lookedup_patchset:
1164 patchset = RunGit(['config', self._codereview_impl.PatchsetSetting()], 1137 patchset = RunGit(['config', self._PatchsetSetting()],
1165 error_ok=True).strip() 1138 error_ok=True).strip()
1166 self.patchset = int(patchset) or None if patchset else None 1139 self.patchset = int(patchset) or None if patchset else None
1167 self.lookedup_patchset = True 1140 self.lookedup_patchset = True
1168 return self.patchset 1141 return self.patchset
1169 1142
1170 def SetPatchset(self, patchset): 1143 def SetPatchset(self, patchset):
1171 """Set this branch's patchset. If patchset=0, clears the patchset.""" 1144 """Set this branch's patchset. If patchset=0, clears the patchset."""
1172 patchset_setting = self._codereview_impl.PatchsetSetting()
1173 if patchset: 1145 if patchset:
1174 RunGit(['config', patchset_setting, str(patchset)]) 1146 RunGit(['config', self._PatchsetSetting(), str(patchset)])
1175 self.patchset = patchset 1147 self.patchset = patchset
1176 else: 1148 else:
1177 RunGit(['config', '--unset', patchset_setting], 1149 RunGit(['config', '--unset', self._PatchsetSetting()],
1178 stderr=subprocess2.PIPE, error_ok=True) 1150 stderr=subprocess2.PIPE, error_ok=True)
1179 self.patchset = None 1151 self.patchset = None
1180 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
1181 def SetIssue(self, issue=None): 1175 def SetIssue(self, issue=None):
1182 """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."""
1183 issue_setting = self._codereview_impl.IssueSetting()
1184 codereview_setting = self._codereview_impl.GetCodereviewServerSetting()
1185 if issue: 1177 if issue:
1186 self.issue = issue 1178 self.issue = issue
1187 RunGit(['config', issue_setting, str(issue)]) 1179 RunGit(['config', self._IssueSetting(), str(issue)])
1188 codereview_server = self._codereview_impl.GetCodereviewServer() 1180 if not settings.GetIsGerrit() and self.rietveld_server:
1189 if codereview_server: 1181 RunGit(['config', self._RietveldServer(), self.rietveld_server])
1190 RunGit(['config', codereview_setting, codereview_server])
1191 else: 1182 else:
1192 current_issue = self.GetIssue() 1183 current_issue = self.GetIssue()
1193 if current_issue: 1184 if current_issue:
1194 RunGit(['config', '--unset', issue_setting]) 1185 RunGit(['config', '--unset', self._IssueSetting()])
1195 self.issue = None 1186 self.issue = None
1196 self.SetPatchset(None) 1187 self.SetPatchset(None)
1197 1188
1198 def GetChange(self, upstream_branch, author): 1189 def GetChange(self, upstream_branch, author):
1199 if not self.GitSanityChecks(upstream_branch): 1190 if not self.GitSanityChecks(upstream_branch):
1200 DieWithError('\nGit sanity check failure') 1191 DieWithError('\nGit sanity check failure')
1201 1192
1202 root = settings.GetRelativeRoot() 1193 root = settings.GetRelativeRoot()
1203 if not root: 1194 if not root:
1204 root = '.' 1195 root = '.'
(...skipping 29 matching lines...) Expand all
1234 return presubmit_support.GitChange( 1225 return presubmit_support.GitChange(
1235 name, 1226 name,
1236 description, 1227 description,
1237 absroot, 1228 absroot,
1238 files, 1229 files,
1239 issue, 1230 issue,
1240 patchset, 1231 patchset,
1241 author, 1232 author,
1242 upstream=upstream_branch) 1233 upstream=upstream_branch)
1243 1234
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
1295 def GetStatus(self): 1235 def GetStatus(self):
1296 """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
1297 or CQ status, assuming adherence to a common workflow. 1237 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.
1425 1238
1426 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:
1427 * 'error' - error from review tool (including deleted issues) 1240 * 'error' - error from review tool (including deleted issues)
1428 * 'unsent' - not sent for review 1241 * 'unsent' - not sent for review
1429 * 'waiting' - waiting for review 1242 * 'waiting' - waiting for review
1430 * 'reply' - waiting for owner to reply to review 1243 * 'reply' - waiting for owner to reply to review
1431 * 'lgtm' - LGTM from at least one approved reviewer 1244 * 'lgtm' - LGTM from at least one approved reviewer
1432 * 'commit' - in the commit queue 1245 * 'commit' - in the commit queue
1433 * 'closed' - closed 1246 * 'closed' - closed
1434 """ 1247 """
(...skipping 24 matching lines...) Expand all
1459 messages = props.get('messages') or [] 1272 messages = props.get('messages') or []
1460 1273
1461 if not messages: 1274 if not messages:
1462 # No message was sent. 1275 # No message was sent.
1463 return 'unsent' 1276 return 'unsent'
1464 if messages[-1]['sender'] != props.get('owner_email'): 1277 if messages[-1]['sender'] != props.get('owner_email'):
1465 # Non-LGTM reply from non-owner 1278 # Non-LGTM reply from non-owner
1466 return 'reply' 1279 return 'reply'
1467 return 'waiting' 1280 return 'waiting'
1468 1281
1469 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
1470 return self.RpcServer().update_description( 1297 return self.RpcServer().update_description(
1471 self.GetIssue(), self.description) 1298 self.GetIssue(), self.description)
1472 1299
1473 def CloseIssue(self): 1300 def CloseIssue(self):
1301 """Updates the description and closes the issue."""
1474 return self.RpcServer().close_issue(self.GetIssue()) 1302 return self.RpcServer().close_issue(self.GetIssue())
1475 1303
1476 def SetFlag(self, flag, value): 1304 def SetFlag(self, flag, value):
1477 """Patchset must match.""" 1305 """Patchset must match."""
1478 if not self.GetPatchset(): 1306 if not self.GetPatchset():
1479 DieWithError('The patchset needs to match. Send another patchset.') 1307 DieWithError('The patchset needs to match. Send another patchset.')
1480 try: 1308 try:
1481 return self.RpcServer().set_flag( 1309 return self.RpcServer().set_flag(
1482 self.GetIssue(), self.GetPatchset(), flag, value) 1310 self.GetIssue(), self.GetPatchset(), flag, value)
1483 except urllib2.HTTPError, e: 1311 except urllib2.HTTPError, e:
1484 if e.code == 404: 1312 if e.code == 404:
1485 DieWithError('The issue %s doesn\'t exist.' % self.GetIssue()) 1313 DieWithError('The issue %s doesn\'t exist.' % self.GetIssue())
1486 if e.code == 403: 1314 if e.code == 403:
1487 DieWithError( 1315 DieWithError(
1488 ('Access denied to issue %s. Maybe the patchset %s doesn\'t ' 1316 ('Access denied to issue %s. Maybe the patchset %s doesn\'t '
1489 'match?') % (self.GetIssue(), self.GetPatchset())) 1317 'match?') % (self.GetIssue(), self.GetPatchset()))
1490 raise 1318 raise
1491 1319
1492 def RpcServer(self): 1320 def RpcServer(self):
1493 """Returns an upload.RpcServer() to access this review's rietveld instance. 1321 """Returns an upload.RpcServer() to access this review's rietveld instance.
1494 """ 1322 """
1495 if not self._rpc_server: 1323 if not self._rpc_server:
1496 self._rpc_server = rietveld.CachingRietveld( 1324 self._rpc_server = rietveld.CachingRietveld(
1497 self.GetCodereviewServer(), 1325 self.GetRietveldServer(),
1498 self._auth_config or auth.make_auth_config()) 1326 self._auth_config or auth.make_auth_config())
1499 return self._rpc_server 1327 return self._rpc_server
1500 1328
1501 def IssueSetting(self): 1329 def _IssueSetting(self):
1502 """Return the git setting that stores this change's issue.""" 1330 """Return the git setting that stores this change's issue."""
1503 return 'branch.%s.rietveldissue' % self.GetBranch() 1331 return 'branch.%s.rietveldissue' % self.GetBranch()
1504 1332
1505 def PatchsetSetting(self): 1333 def _PatchsetSetting(self):
1506 """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."""
1507 return 'branch.%s.rietveldpatchset' % self.GetBranch() 1335 return 'branch.%s.rietveldpatchset' % self.GetBranch()
1508 1336
1509 def GetCodereviewServerSetting(self): 1337 def _RietveldServer(self):
1510 """Returns the git setting that stores this change's rietveld server.""" 1338 """Returns the git setting that stores this change's rietveld server."""
1511 branch = self.GetBranch() 1339 branch = self.GetBranch()
1512 if branch: 1340 if branch:
1513 return 'branch.%s.rietveldserver' % branch 1341 return 'branch.%s.rietveldserver' % branch
1514 return None 1342 return None
1515 1343
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
1601 1344
1602 class ChangeDescription(object): 1345 class ChangeDescription(object):
1603 """Contains a parsed form of the change description.""" 1346 """Contains a parsed form of the change description."""
1604 R_LINE = r'^[ \t]*(TBR|R)[ \t]*=[ \t]*(.*?)[ \t]*$' 1347 R_LINE = r'^[ \t]*(TBR|R)[ \t]*=[ \t]*(.*?)[ \t]*$'
1605 BUG_LINE = r'^[ \t]*(BUG)[ \t]*=[ \t]*(.*?)[ \t]*$' 1348 BUG_LINE = r'^[ \t]*(BUG)[ \t]*=[ \t]*(.*?)[ \t]*$'
1606 1349
1607 def __init__(self, description): 1350 def __init__(self, description):
1608 self._description_lines = (description or '').strip().splitlines() 1351 self._description_lines = (description or '').strip().splitlines()
1609 1352
1610 @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
2136 return 0 1879 return 0
2137 1880
2138 branches = RunGit(['for-each-ref', '--format=%(refname)', 'refs/heads']) 1881 branches = RunGit(['for-each-ref', '--format=%(refname)', 'refs/heads'])
2139 if not branches: 1882 if not branches:
2140 print('No local branch found.') 1883 print('No local branch found.')
2141 return 0 1884 return 0
2142 1885
2143 changes = ( 1886 changes = (
2144 Changelist(branchref=b, auth_config=auth_config) 1887 Changelist(branchref=b, auth_config=auth_config)
2145 for b in branches.splitlines()) 1888 for b in branches.splitlines())
2146 # TODO(tandrii): refactor to use CLs list instead of branches list.
2147 branches = [c.GetBranch() for c in changes] 1889 branches = [c.GetBranch() for c in changes]
2148 alignment = max(5, max(len(b) for b in branches)) 1890 alignment = max(5, max(len(b) for b in branches))
2149 print 'Branches associated with reviews:' 1891 print 'Branches associated with reviews:'
2150 output = get_cl_statuses(branches, 1892 output = get_cl_statuses(branches,
2151 fine_grained=not options.fast, 1893 fine_grained=not options.fast,
2152 max_processes=options.maxjobs, 1894 max_processes=options.maxjobs,
2153 auth_config=auth_config) 1895 auth_config=auth_config)
2154 1896
2155 branch_statuses = {} 1897 branch_statuses = {}
2156 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
2252 options, args = parser.parse_args(args) 1994 options, args = parser.parse_args(args)
2253 auth_config = auth.extract_auth_config_from_options(options) 1995 auth_config = auth.extract_auth_config_from_options(options)
2254 1996
2255 issue = None 1997 issue = None
2256 if options.issue: 1998 if options.issue:
2257 try: 1999 try:
2258 issue = int(options.issue) 2000 issue = int(options.issue)
2259 except ValueError: 2001 except ValueError:
2260 DieWithError('A review issue id is expected to be a number') 2002 DieWithError('A review issue id is expected to be a number')
2261 2003
2262 cl = Changelist(issue=issue, codereview='rietveld', auth_config=auth_config) 2004 cl = Changelist(issue=issue, auth_config=auth_config)
2263 2005
2264 if options.comment: 2006 if options.comment:
2265 cl.AddComment(options.comment) 2007 cl.AddComment(options.comment)
2266 return 0 2008 return 0
2267 2009
2268 data = cl.GetIssueProperties() 2010 data = cl.GetIssueProperties()
2269 summary = [] 2011 summary = []
2270 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']):
2271 summary.append({ 2013 summary.append({
2272 'date': message['date'], 2014 'date': message['date'],
(...skipping 358 matching lines...) Expand 10 before | Expand all | Expand 10 after
2631 remote_branch = remote_branch.replace('refs/remotes/', 'refs/') 2373 remote_branch = remote_branch.replace('refs/remotes/', 'refs/')
2632 # If a pending prefix exists then replace refs/ with it. 2374 # If a pending prefix exists then replace refs/ with it.
2633 if pending_prefix: 2375 if pending_prefix:
2634 remote_branch = remote_branch.replace('refs/', pending_prefix) 2376 remote_branch = remote_branch.replace('refs/', pending_prefix)
2635 return remote_branch 2377 return remote_branch
2636 2378
2637 2379
2638 def RietveldUpload(options, args, cl, change): 2380 def RietveldUpload(options, args, cl, change):
2639 """upload the patch to rietveld.""" 2381 """upload the patch to rietveld."""
2640 upload_args = ['--assume_yes'] # Don't ask about untracked files. 2382 upload_args = ['--assume_yes'] # Don't ask about untracked files.
2641 upload_args.extend(['--server', cl.GetCodereviewServer()]) 2383 upload_args.extend(['--server', cl.GetRietveldServer()])
2642 # TODO(tandrii): refactor this ugliness into _RietveldChangelistImpl. 2384 upload_args.extend(auth.auth_config_to_command_options(cl.auth_config))
2643 upload_args.extend(auth.auth_config_to_command_options(
2644 cl._codereview_impl.GetAuthConfig()))
2645 if options.emulate_svn_auto_props: 2385 if options.emulate_svn_auto_props:
2646 upload_args.append('--emulate_svn_auto_props') 2386 upload_args.append('--emulate_svn_auto_props')
2647 2387
2648 change_desc = None 2388 change_desc = None
2649 2389
2650 if options.email is not None: 2390 if options.email is not None:
2651 upload_args.extend(['--email', options.email]) 2391 upload_args.extend(['--email', options.email])
2652 2392
2653 if cl.GetIssue(): 2393 if cl.GetIssue():
2654 if options.title: 2394 if options.title:
(...skipping 222 matching lines...) Expand 10 before | Expand all | Expand 10 after
2877 2617
2878 # Default to diffing against common ancestor of upstream branch 2618 # Default to diffing against common ancestor of upstream branch
2879 base_branch = cl.GetCommonAncestorWithUpstream() 2619 base_branch = cl.GetCommonAncestorWithUpstream()
2880 args = [base_branch, 'HEAD'] 2620 args = [base_branch, 'HEAD']
2881 2621
2882 # Make sure authenticated to Rietveld before running expensive hooks. It is 2622 # Make sure authenticated to Rietveld before running expensive hooks. It is
2883 # a fast, best efforts check. Rietveld still can reject the authentication 2623 # a fast, best efforts check. Rietveld still can reject the authentication
2884 # during the actual upload. 2624 # during the actual upload.
2885 if not settings.GetIsGerrit() and auth_config.use_oauth2: 2625 if not settings.GetIsGerrit() and auth_config.use_oauth2:
2886 authenticator = auth.get_authenticator_for_host( 2626 authenticator = auth.get_authenticator_for_host(
2887 cl.GetCodereviewServer(), auth_config) 2627 cl.GetRietveldServer(), auth_config)
2888 if not authenticator.has_cached_credentials(): 2628 if not authenticator.has_cached_credentials():
2889 raise auth.LoginRequiredError(cl.GetCodereviewServer()) 2629 raise auth.LoginRequiredError(cl.GetRietveldServer())
2890 2630
2891 # Apply watchlists on upload. 2631 # Apply watchlists on upload.
2892 change = cl.GetChange(base_branch, None) 2632 change = cl.GetChange(base_branch, None)
2893 watchlist = watchlists.Watchlists(change.RepositoryRoot()) 2633 watchlist = watchlists.Watchlists(change.RepositoryRoot())
2894 files = [f.LocalPath() for f in change.AffectedFiles()] 2634 files = [f.LocalPath() for f in change.AffectedFiles()]
2895 if not options.bypass_watchlists: 2635 if not options.bypass_watchlists:
2896 cl.SetWatchers(watchlist.GetWatchersForPaths(files)) 2636 cl.SetWatchers(watchlist.GetWatchersForPaths(files))
2897 2637
2898 if not options.bypass_hooks: 2638 if not options.bypass_hooks:
2899 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
3473 return PatchIssue(issue_arg, options.reject, options.nocommit, 3213 return PatchIssue(issue_arg, options.reject, options.nocommit,
3474 options.directory, auth_config) 3214 options.directory, auth_config)
3475 3215
3476 3216
3477 def PatchIssue(issue_arg, reject, nocommit, directory, auth_config): 3217 def PatchIssue(issue_arg, reject, nocommit, directory, auth_config):
3478 # 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
3479 # 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
3480 # consequences of the caller not checking this could be dire. 3220 # consequences of the caller not checking this could be dire.
3481 assert(not git_common.is_dirty_git_tree('apply')) 3221 assert(not git_common.is_dirty_git_tree('apply'))
3482 3222
3483 # TODO(tandrii): implement for Gerrit.
3484 if type(issue_arg) is int or issue_arg.isdigit(): 3223 if type(issue_arg) is int or issue_arg.isdigit():
3485 # Input is an issue id. Figure out the URL. 3224 # Input is an issue id. Figure out the URL.
3486 issue = int(issue_arg) 3225 issue = int(issue_arg)
3487 cl = Changelist(issue=issue, codereview='rietveld', auth_config=auth_config) 3226 cl = Changelist(issue=issue, auth_config=auth_config)
3488 patchset = cl.GetMostRecentPatchset() 3227 patchset = cl.GetMostRecentPatchset()
3489 patch_data = cl._codereview_impl.GetPatchSetDiff(issue, patchset) 3228 patch_data = cl.GetPatchSetDiff(issue, patchset)
3490 else: 3229 else:
3491 # Assume it's a URL to the patch. Default to https. 3230 # Assume it's a URL to the patch. Default to https.
3492 issue_url = gclient_utils.UpgradeToHttps(issue_arg) 3231 issue_url = gclient_utils.UpgradeToHttps(issue_arg)
3493 match = re.match(r'(.*?)/download/issue(\d+)_(\d+).diff', issue_url) 3232 match = re.match(r'(.*?)/download/issue(\d+)_(\d+).diff', issue_url)
3494 if not match: 3233 if not match:
3495 DieWithError('Must pass an issue ID or full URL for ' 3234 DieWithError('Must pass an issue ID or full URL for '
3496 '\'Download raw patch set\'') 3235 '\'Download raw patch set\'')
3497 issue = int(match.group(2)) 3236 issue = int(match.group(2))
3498 cl = Changelist(issue=issue, codereview='rietveld', 3237 cl = Changelist(issue=issue, auth_config=auth_config)
3499 rietvled_server=match.group(1), auth_config=auth_config) 3238 cl.rietveld_server = match.group(1)
3500 patchset = int(match.group(3)) 3239 patchset = int(match.group(3))
3501 patch_data = urllib2.urlopen(issue_arg).read() 3240 patch_data = urllib2.urlopen(issue_arg).read()
3502 3241
3503 # 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
3504 # applying the patch. 3243 # applying the patch.
3505 top = settings.GetRelativeRoot() 3244 top = settings.GetRelativeRoot()
3506 if top: 3245 if top:
3507 os.chdir(top) 3246 os.chdir(top)
3508 3247
3509 # 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
3533 except subprocess2.CalledProcessError: 3272 except subprocess2.CalledProcessError:
3534 print 'Failed to apply the patch' 3273 print 'Failed to apply the patch'
3535 return 1 3274 return 1
3536 3275
3537 # 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.
3538 if not nocommit: 3277 if not nocommit:
3539 RunGit(['commit', '-m', (cl.GetDescription() + '\n\n' + 3278 RunGit(['commit', '-m', (cl.GetDescription() + '\n\n' +
3540 'patch from issue %(i)s at patchset ' 3279 'patch from issue %(i)s at patchset '
3541 '%(p)s (http://crrev.com/%(i)s#ps%(p)s)' 3280 '%(p)s (http://crrev.com/%(i)s#ps%(p)s)'
3542 % {'i': issue, 'p': patchset})]) 3281 % {'i': issue, 'p': patchset})])
3543 cl = Changelist(codereview='rietveld', auth_config=auth_config, 3282 cl = Changelist(auth_config=auth_config)
3544 rietveld_server=cl.GetCodereviewServer())
3545 cl.SetIssue(issue) 3283 cl.SetIssue(issue)
3546 cl.SetPatchset(patchset) 3284 cl.SetPatchset(patchset)
3547 print "Committed patch locally." 3285 print "Committed patch locally."
3548 else: 3286 else:
3549 print "Patch applied to index." 3287 print "Patch applied to index."
3550 return 0 3288 return 0
3551 3289
3552 3290
3553 def CMDrebase(parser, args): 3291 def CMDrebase(parser, args):
3554 """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
4269 if __name__ == '__main__': 4007 if __name__ == '__main__':
4270 # 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
4271 # unit testing. 4009 # unit testing.
4272 fix_encoding.fix_encoding() 4010 fix_encoding.fix_encoding()
4273 colorama.init() 4011 colorama.init()
4274 try: 4012 try:
4275 sys.exit(main(sys.argv[1:])) 4013 sys.exit(main(sys.argv[1:]))
4276 except KeyboardInterrupt: 4014 except KeyboardInterrupt:
4277 sys.stderr.write('interrupted\n') 4015 sys.stderr.write('interrupted\n')
4278 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