summaryrefslogtreecommitdiffstats
path: root/subcmds/upload.py
diff options
context:
space:
mode:
authorShawn O. Pearce <sop@google.com>2010-12-07 10:31:19 -0800
committerShawn O. Pearce <sop@google.com>2010-12-07 11:13:29 -0800
commit13f3da50d40b89ee5b05f5f3de9542c20edac6d1 (patch)
treed085b6f6b498bde85a1969fce884dd24e88d03d5 /subcmds/upload.py
parent3218c13205694434edb2375ab8a8515554eed366 (diff)
parent2b8db3ce3e7344b9f3b5216637c5af0d54be5656 (diff)
downloadgit-repo-13f3da50d40b89ee5b05f5f3de9542c20edac6d1.tar.gz
Merge branch 'stable'
* stable: (33 commits) Added feature to print a <notice> from manifest at the end of a sync. sync: Use --force-broken to continue other projects upload: Remove --replace option sync --quiet: be more quiet sync: Enable use of git clone --reference Only delete corrupt pickle config files if they exist Don't allow git fetch to start ControlMaster Check for existing SSH ControlMaster Fix for handling values of EDITOR which contain a space. upload: Fix --replace flag rebase: Pass through more options upload: Allow review.HOST.username to override email upload -t: Automatically include local branch name Warn users before uploading if there are local changes sync: Try fetching a tag as a last resort before giving up rebase: Automatically rebase branch on upstrea upload: Automatically --cc folks in review.URL.autocopy Fix format string bugs in grep Do not invoke ssh with -p argument when no port has been specified. Allow files to be copied into new folders ... Conflicts: git_config.py manifest_xml.py subcmds/init.py subcmds/sync.py subcmds/upload.py Change-Id: I4756a6908277e91505c35287a122a775b68f4df5
Diffstat (limited to 'subcmds/upload.py')
-rw-r--r--subcmds/upload.py164
1 files changed, 83 insertions, 81 deletions
diff --git a/subcmds/upload.py b/subcmds/upload.py
index 2ab6a484..20822096 100644
--- a/subcmds/upload.py
+++ b/subcmds/upload.py
@@ -13,6 +13,7 @@
13# See the License for the specific language governing permissions and 13# See the License for the specific language governing permissions and
14# limitations under the License. 14# limitations under the License.
15 15
16import copy
16import re 17import re
17import sys 18import sys
18 19
@@ -20,6 +21,17 @@ from command import InteractiveCommand
20from editor import Editor 21from editor import Editor
21from error import UploadError 22from error import UploadError
22 23
24UNUSUAL_COMMIT_THRESHOLD = 5
25
26def _ConfirmManyUploads(multiple_branches=False):
27 if multiple_branches:
28 print "ATTENTION: One or more branches has an unusually high number of commits."
29 else:
30 print "ATTENTION: You are uploading an unusually high number of commits."
31 print "YOU PROBABLY DO NOT MEAN TO DO THIS. (Did you rebase across branches?)"
32 answer = raw_input("If you are sure you intend to do this, type 'yes': ").strip()
33 return answer == "yes"
34
23def _die(fmt, *args): 35def _die(fmt, *args):
24 msg = fmt % args 36 msg = fmt % args
25 print >>sys.stderr, 'error: %s' % msg 37 print >>sys.stderr, 'error: %s' % msg
@@ -35,7 +47,7 @@ class Upload(InteractiveCommand):
35 common = True 47 common = True
36 helpSummary = "Upload changes for code review" 48 helpSummary = "Upload changes for code review"
37 helpUsage=""" 49 helpUsage="""
38%prog [--re --cc] {[<project>]... | --replace <project>} 50%prog [--re --cc] [<project>]...
39""" 51"""
40 helpDescription = """ 52 helpDescription = """
41The '%prog' command is used to send changes to the Gerrit Code 53The '%prog' command is used to send changes to the Gerrit Code
@@ -55,12 +67,6 @@ added to the respective list of users, and emails are sent to any
55new users. Users passed as --reviewers must already be registered 67new users. Users passed as --reviewers must already be registered
56with the code review system, or the upload will fail. 68with the code review system, or the upload will fail.
57 69
58If the --replace option (deprecated) is passed the user can designate
59which existing change(s) in Gerrit match up to the commits in the
60branch being uploaded. For each matched pair of change,commit the
61commit will be added as a new patch set, completely replacing the
62set of files and description associated with the change in Gerrit.
63
64Configuration 70Configuration
65------------- 71-------------
66 72
@@ -72,6 +78,19 @@ to "true" then repo will assume you always answer "y" at the prompt,
72and will not prompt you further. If it is set to "false" then repo 78and will not prompt you further. If it is set to "false" then repo
73will assume you always answer "n", and will abort. 79will assume you always answer "n", and will abort.
74 80
81review.URL.autocopy:
82
83To automatically copy a user or mailing list to all uploaded reviews,
84you can set a per-project or global Git option to do so. Specifically,
85review.URL.autocopy can be set to a comma separated list of reviewers
86who you always want copied on all uploads with a non-empty --re
87argument.
88
89review.URL.username:
90
91Override the username used to connect to Gerrit Code Review.
92By default the local part of the email address is used.
93
75The URL must match the review URL listed in the manifest XML file, 94The URL must match the review URL listed in the manifest XML file,
76or in the .git/config within the project. For example: 95or in the .git/config within the project. For example:
77 96
@@ -81,6 +100,7 @@ or in the .git/config within the project. For example:
81 100
82 [review "http://review.example.com/"] 101 [review "http://review.example.com/"]
83 autoupload = true 102 autoupload = true
103 autocopy = johndoe@company.com,my-team-alias@company.com
84 104
85References 105References
86---------- 106----------
@@ -90,9 +110,9 @@ Gerrit Code Review: http://code.google.com/p/gerrit/
90""" 110"""
91 111
92 def _Options(self, p): 112 def _Options(self, p):
93 p.add_option('--replace', 113 p.add_option('-t',
94 dest='replace', action='store_true', 114 dest='auto_topic', action='store_true',
95 help='Upload replacement patchsets from this branch (deprecated)') 115 help='Send local branch name to Gerrit Code Review')
96 p.add_option('--re', '--reviewers', 116 p.add_option('--re', '--reviewers',
97 type='string', action='append', dest='reviewers', 117 type='string', action='append', dest='reviewers',
98 help='Request reviews from these people.') 118 help='Request reviews from these people.')
@@ -100,7 +120,7 @@ Gerrit Code Review: http://code.google.com/p/gerrit/
100 type='string', action='append', dest='cc', 120 type='string', action='append', dest='cc',
101 help='Also send email to these email addresses.') 121 help='Also send email to these email addresses.')
102 122
103 def _SingleBranch(self, branch, people): 123 def _SingleBranch(self, opt, branch, people):
104 project = branch.project 124 project = branch.project
105 name = branch.name 125 name = branch.name
106 remote = project.GetBranch(name).remote 126 remote = project.GetBranch(name).remote
@@ -129,11 +149,15 @@ Gerrit Code Review: http://code.google.com/p/gerrit/
129 answer = answer in ('y', 'Y', 'yes', '1', 'true', 't') 149 answer = answer in ('y', 'Y', 'yes', '1', 'true', 't')
130 150
131 if answer: 151 if answer:
132 self._UploadAndReport([branch], people) 152 if len(branch.commits) > UNUSUAL_COMMIT_THRESHOLD:
153 answer = _ConfirmManyUploads()
154
155 if answer:
156 self._UploadAndReport(opt, [branch], people)
133 else: 157 else:
134 _die("upload aborted by user") 158 _die("upload aborted by user")
135 159
136 def _MultipleBranches(self, pending, people): 160 def _MultipleBranches(self, opt, pending, people):
137 projects = {} 161 projects = {}
138 branches = {} 162 branches = {}
139 163
@@ -192,7 +216,30 @@ Gerrit Code Review: http://code.google.com/p/gerrit/
192 todo.append(branch) 216 todo.append(branch)
193 if not todo: 217 if not todo:
194 _die("nothing uncommented for upload") 218 _die("nothing uncommented for upload")
195 self._UploadAndReport(todo, people) 219
220 many_commits = False
221 for branch in todo:
222 if len(branch.commits) > UNUSUAL_COMMIT_THRESHOLD:
223 many_commits = True
224 break
225 if many_commits:
226 if not _ConfirmManyUploads(multiple_branches=True):
227 _die("upload aborted by user")
228
229 self._UploadAndReport(opt, todo, people)
230
231 def _AppendAutoCcList(self, branch, people):
232 """
233 Appends the list of users in the CC list in the git project's config if a
234 non-empty reviewer list was found.
235 """
236
237 name = branch.name
238 project = branch.project
239 key = 'review.%s.autocopy' % project.GetBranch(name).remote.review
240 raw_list = project.config.GetString(key)
241 if not raw_list is None and len(people[0]) > 0:
242 people[1].extend([entry.strip() for entry in raw_list.split(',')])
196 243
197 def _FindGerritChange(self, branch): 244 def _FindGerritChange(self, branch):
198 last_pub = branch.project.WasPublished(branch.name) 245 last_pub = branch.project.WasPublished(branch.name)
@@ -206,66 +253,29 @@ Gerrit Code Review: http://code.google.com/p/gerrit/
206 except: 253 except:
207 return "" 254 return ""
208 255
209 def _ReplaceBranch(self, project, people): 256 def _UploadAndReport(self, opt, todo, original_people):
210 branch = project.CurrentBranch
211 if not branch:
212 print >>sys.stdout, "no branches ready for upload"
213 return
214 branch = project.GetUploadableBranch(branch)
215 if not branch:
216 print >>sys.stdout, "no branches ready for upload"
217 return
218
219 script = []
220 script.append('# Replacing from branch %s' % branch.name)
221
222 if len(branch.commits) == 1:
223 change = self._FindGerritChange(branch)
224 script.append('[%-6s] %s' % (change, branch.commits[0]))
225 else:
226 for commit in branch.commits:
227 script.append('[ ] %s' % commit)
228
229 script.append('')
230 script.append('# Insert change numbers in the brackets to add a new patch set.')
231 script.append('# To create a new change record, leave the brackets empty.')
232
233 script = Editor.EditString("\n".join(script)).split("\n")
234
235 change_re = re.compile(r'^\[\s*(\d{1,})\s*\]\s*([0-9a-f]{1,}) .*$')
236 to_replace = dict()
237 full_hashes = branch.unabbrev_commits
238
239 for line in script:
240 m = change_re.match(line)
241 if m:
242 c = m.group(1)
243 f = m.group(2)
244 try:
245 f = full_hashes[f]
246 except KeyError:
247 print 'fh = %s' % full_hashes
248 print >>sys.stderr, "error: commit %s not found" % f
249 sys.exit(1)
250 if c in to_replace:
251 print >>sys.stderr,\
252 "error: change %s cannot accept multiple commits" % c
253 sys.exit(1)
254 to_replace[c] = f
255
256 if not to_replace:
257 print >>sys.stderr, "error: no replacements specified"
258 print >>sys.stderr, " use 'repo upload' without --replace"
259 sys.exit(1)
260
261 branch.replace_changes = to_replace
262 self._UploadAndReport([branch], people)
263
264 def _UploadAndReport(self, todo, people):
265 have_errors = False 257 have_errors = False
266 for branch in todo: 258 for branch in todo:
267 try: 259 try:
268 branch.UploadForReview(people) 260 people = copy.deepcopy(original_people)
261 self._AppendAutoCcList(branch, people)
262
263 # Check if there are local changes that may have been forgotten
264 if branch.project.HasChanges():
265 key = 'review.%s.autoupload' % branch.project.remote.review
266 answer = branch.project.config.GetBoolean(key)
267
268 # if they want to auto upload, let's not ask because it could be automated
269 if answer is None:
270 sys.stdout.write('Uncommitted changes in ' + branch.project.name + ' (did you forget to amend?). Continue uploading? (y/n) ')
271 a = sys.stdin.readline().strip().lower()
272 if a not in ('y', 'yes', 't', 'true', 'on'):
273 print >>sys.stderr, "skipping upload"
274 branch.uploaded = False
275 branch.error = 'User aborted'
276 continue
277
278 branch.UploadForReview(people, auto_topic=opt.auto_topic)
269 branch.uploaded = True 279 branch.uploaded = True
270 except UploadError, e: 280 except UploadError, e:
271 branch.error = e 281 branch.error = e
@@ -309,14 +319,6 @@ Gerrit Code Review: http://code.google.com/p/gerrit/
309 cc = _SplitEmails(opt.cc) 319 cc = _SplitEmails(opt.cc)
310 people = (reviewers,cc) 320 people = (reviewers,cc)
311 321
312 if opt.replace:
313 if len(project_list) != 1:
314 print >>sys.stderr, \
315 'error: --replace requires exactly one project'
316 sys.exit(1)
317 self._ReplaceBranch(project_list[0], people)
318 return
319
320 for project in project_list: 322 for project in project_list:
321 avail = project.GetUploadableBranches() 323 avail = project.GetUploadableBranches()
322 if avail: 324 if avail:
@@ -325,6 +327,6 @@ Gerrit Code Review: http://code.google.com/p/gerrit/
325 if not pending: 327 if not pending:
326 print >>sys.stdout, "no branches ready for upload" 328 print >>sys.stdout, "no branches ready for upload"
327 elif len(pending) == 1 and len(pending[0][1]) == 1: 329 elif len(pending) == 1 and len(pending[0][1]) == 1:
328 self._SingleBranch(pending[0][1][0], people) 330 self._SingleBranch(opt, pending[0][1][0], people)
329 else: 331 else:
330 self._MultipleBranches(pending, people) 332 self._MultipleBranches(opt, pending, people)