LEFT | RIGHT |
1 # coding=utf-8 | 1 # coding=utf-8 |
2 # (The line above is necessary so that I can use 世界 in the | 2 # (The line above is necessary so that I can use 世界 in the |
3 # *comment* below without Python getting all bent out of shape.) | 3 # *comment* below without Python getting all bent out of shape.) |
4 | 4 |
5 # Copyright 2007-2009 Google Inc. | 5 # Copyright 2007-2009 Google Inc. |
6 # | 6 # |
7 # Licensed under the Apache License, Version 2.0 (the "License"); | 7 # Licensed under the Apache License, Version 2.0 (the "License"); |
8 # you may not use this file except in compliance with the License. | 8 # you may not use this file except in compliance with the License. |
9 # You may obtain a copy of the License at | 9 # You may obtain a copy of the License at |
10 # | 10 # |
(...skipping 235 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
246 s += "\tReviewer: " + JoinComma(cl.reviewer) + "\n" | 246 s += "\tReviewer: " + JoinComma(cl.reviewer) + "\n" |
247 s += "\tCC: " + JoinComma(cl.cc) + "\n" | 247 s += "\tCC: " + JoinComma(cl.cc) + "\n" |
248 s += "\tFiles:\n" | 248 s += "\tFiles:\n" |
249 for f in cl.files: | 249 for f in cl.files: |
250 s += "\t\t" + f + "\n" | 250 s += "\t\t" + f + "\n" |
251 typecheck(s, str) | 251 typecheck(s, str) |
252 return s | 252 return s |
253 | 253 |
254 def Flush(self, ui, repo): | 254 def Flush(self, ui, repo): |
255 if self.name == "new": | 255 if self.name == "new": |
256 » » » self.Upload(ui, repo, gofmt_just_warn=True) | 256 » » » self.Upload(ui, repo, gofmt_just_warn=True, creating=Tru
e) |
257 dir = CodeReviewDir(ui, repo) | 257 dir = CodeReviewDir(ui, repo) |
258 path = dir + '/cl.' + self.name | 258 path = dir + '/cl.' + self.name |
259 f = open(path+'!', "w") | 259 f = open(path+'!', "w") |
260 f.write(self.DiskText()) | 260 f.write(self.DiskText()) |
261 f.close() | 261 f.close() |
262 if sys.platform == "win32" and os.path.isfile(path): | 262 if sys.platform == "win32" and os.path.isfile(path): |
263 os.remove(path) | 263 os.remove(path) |
264 os.rename(path+'!', path) | 264 os.rename(path+'!', path) |
265 if self.web and not self.copied_from: | 265 if self.web and not self.copied_from: |
266 EditDesc(self.name, desc=self.desc, | 266 EditDesc(self.name, desc=self.desc, |
267 reviewers=JoinComma(self.reviewer), cc=JoinComma
(self.cc)) | 267 reviewers=JoinComma(self.reviewer), cc=JoinComma
(self.cc)) |
268 | 268 |
269 def Delete(self, ui, repo): | 269 def Delete(self, ui, repo): |
270 dir = CodeReviewDir(ui, repo) | 270 dir = CodeReviewDir(ui, repo) |
271 os.unlink(dir + "/cl." + self.name) | 271 os.unlink(dir + "/cl." + self.name) |
272 | 272 |
273 def Subject(self): | 273 def Subject(self): |
274 s = line1(self.desc) | 274 s = line1(self.desc) |
275 if len(s) > 60: | 275 if len(s) > 60: |
276 s = s[0:55] + "..." | 276 s = s[0:55] + "..." |
277 if self.name != "new": | 277 if self.name != "new": |
278 s = "code review %s: %s" % (self.name, s) | 278 s = "code review %s: %s" % (self.name, s) |
279 typecheck(s, str) | 279 typecheck(s, str) |
280 return s | 280 return s |
281 | 281 |
282 » def Upload(self, ui, repo, send_mail=False, gofmt=True, gofmt_just_warn=
False): | 282 » def Upload(self, ui, repo, send_mail=False, gofmt=True, gofmt_just_warn=
False, creating=False, quiet=False): |
283 » » if not self.files: | 283 » » if not self.files and not creating: |
284 ui.warn("no files in change list\n") | 284 ui.warn("no files in change list\n") |
285 if ui.configbool("codereview", "force_gofmt", True) and gofmt: | 285 if ui.configbool("codereview", "force_gofmt", True) and gofmt: |
286 CheckFormat(ui, repo, self.files, just_warn=gofmt_just_w
arn) | 286 CheckFormat(ui, repo, self.files, just_warn=gofmt_just_w
arn) |
287 set_status("uploading CL metadata + diffs") | 287 set_status("uploading CL metadata + diffs") |
288 os.chdir(repo.root) | 288 os.chdir(repo.root) |
289 form_fields = [ | 289 form_fields = [ |
290 ("content_upload", "1"), | 290 ("content_upload", "1"), |
291 ("reviewers", JoinComma(self.reviewer)), | 291 ("reviewers", JoinComma(self.reviewer)), |
292 ("cc", JoinComma(self.cc)), | 292 ("cc", JoinComma(self.cc)), |
293 ("description", self.desc), | 293 ("description", self.desc), |
294 ("base_hashes", ""), | 294 ("base_hashes", ""), |
295 # Would prefer not to change the subject | |
296 # on reupload, but /upload requires it. | |
297 ("subject", self.Subject()), | |
298 ] | 295 ] |
299 | 296 |
300 if self.name != "new": | 297 if self.name != "new": |
301 form_fields.append(("issue", self.name)) | 298 form_fields.append(("issue", self.name)) |
302 vcs = None | 299 vcs = None |
303 » » if self.files: | 300 » » # We do not include files when creating the issue, |
| 301 » » # because we want the patch sets to record the repository |
| 302 » » # and base revision they are diffs against. We use the patch |
| 303 » » # set message for that purpose, but there is no message with |
| 304 » » # the first patch set. Instead the message gets used as the |
| 305 » » # new CL's overall subject. So omit the diffs when creating |
| 306 » » # and then we'll run an immediate upload. |
| 307 » » # This has the effect that every CL begins with an empty "Patch
set 1". |
| 308 » » if self.files and not creating: |
304 vcs = MercurialVCS(upload_options, ui, repo) | 309 vcs = MercurialVCS(upload_options, ui, repo) |
305 data = vcs.GenerateDiff(self.files) | 310 data = vcs.GenerateDiff(self.files) |
306 files = vcs.GetBaseFiles(data) | 311 files = vcs.GetBaseFiles(data) |
307 if len(data) > MAX_UPLOAD_SIZE: | 312 if len(data) > MAX_UPLOAD_SIZE: |
308 uploaded_diff_file = [] | 313 uploaded_diff_file = [] |
309 form_fields.append(("separate_patches", "1")) | 314 form_fields.append(("separate_patches", "1")) |
310 else: | 315 else: |
311 uploaded_diff_file = [("data", "data.diff", data
)] | 316 uploaded_diff_file = [("data", "data.diff", data
)] |
312 else: | 317 else: |
313 uploaded_diff_file = [("data", "data.diff", emptydiff)] | 318 uploaded_diff_file = [("data", "data.diff", emptydiff)] |
| 319 ················ |
| 320 if vcs and self.name != "new": |
| 321 form_fields.append(("subject", "diff -r " + vcs.base_rev
+ " " + getremote(ui, repo, {}).path)) |
| 322 else: |
| 323 # First upload sets the subject for the CL itself. |
| 324 form_fields.append(("subject", self.Subject())) |
314 ctype, body = EncodeMultipartFormData(form_fields, uploaded_diff
_file) | 325 ctype, body = EncodeMultipartFormData(form_fields, uploaded_diff
_file) |
315 response_body = MySend("/upload", body, content_type=ctype) | 326 response_body = MySend("/upload", body, content_type=ctype) |
316 patchset = None | 327 patchset = None |
317 msg = response_body | 328 msg = response_body |
318 lines = msg.splitlines() | 329 lines = msg.splitlines() |
319 if len(lines) >= 2: | 330 if len(lines) >= 2: |
320 msg = lines[0] | 331 msg = lines[0] |
321 patchset = lines[1].strip() | 332 patchset = lines[1].strip() |
322 patches = [x.split(" ", 1) for x in lines[2:]] | 333 patches = [x.split(" ", 1) for x in lines[2:]] |
323 » » ui.status(msg + "\n") | 334 » » if response_body.startswith("Issue updated.") and quiet: |
| 335 » » » pass |
| 336 » » else: |
| 337 » » » ui.status(msg + "\n") |
324 set_status("uploaded CL metadata + diffs") | 338 set_status("uploaded CL metadata + diffs") |
325 if not response_body.startswith("Issue created.") and not respon
se_body.startswith("Issue updated."): | 339 if not response_body.startswith("Issue created.") and not respon
se_body.startswith("Issue updated."): |
326 raise util.Abort("failed to update issue: " + response_b
ody) | 340 raise util.Abort("failed to update issue: " + response_b
ody) |
327 issue = msg[msg.rfind("/")+1:] | 341 issue = msg[msg.rfind("/")+1:] |
328 self.name = issue | 342 self.name = issue |
329 if not self.url: | 343 if not self.url: |
330 self.url = server_url_base + self.name | 344 self.url = server_url_base + self.name |
331 if not uploaded_diff_file: | 345 if not uploaded_diff_file: |
332 set_status("uploading patches") | 346 set_status("uploading patches") |
333 patches = UploadSeparatePatches(issue, rpc, patchset, da
ta, upload_options) | 347 patches = UploadSeparatePatches(issue, rpc, patchset, da
ta, upload_options) |
334 if vcs: | 348 if vcs: |
335 set_status("uploading base files") | 349 set_status("uploading base files") |
336 vcs.UploadBaseFiles(issue, rpc, patches, patchset, uploa
d_options, files) | 350 vcs.UploadBaseFiles(issue, rpc, patches, patchset, uploa
d_options, files) |
337 if send_mail: | 351 if send_mail: |
338 set_status("sending mail") | 352 set_status("sending mail") |
339 MySend("/" + issue + "/mail", payload="") | 353 MySend("/" + issue + "/mail", payload="") |
340 self.web = True | 354 self.web = True |
341 set_status("flushing changes to disk") | 355 set_status("flushing changes to disk") |
342 self.Flush(ui, repo) | 356 self.Flush(ui, repo) |
343 return | 357 return |
344 | 358 |
345 » def Mail(self, ui,repo): | 359 » def Mail(self, ui, repo): |
346 pmsg = "Hello " + JoinComma(self.reviewer) | 360 pmsg = "Hello " + JoinComma(self.reviewer) |
347 if self.cc: | 361 if self.cc: |
348 pmsg += " (cc: %s)" % (', '.join(self.cc),) | 362 pmsg += " (cc: %s)" % (', '.join(self.cc),) |
349 pmsg += ",\n" | 363 pmsg += ",\n" |
350 pmsg += "\n" | 364 pmsg += "\n" |
| 365 repourl = getremote(ui, repo, {}).path |
351 if not self.mailed: | 366 if not self.mailed: |
352 » » » pmsg += "I'd like you to review this change.\n" | 367 » » » pmsg += "I'd like you to review this change to\n" + repo
url + "\n" |
353 else: | 368 else: |
354 pmsg += "Please take another look.\n" | 369 pmsg += "Please take another look.\n" |
355 typecheck(pmsg, str) | 370 typecheck(pmsg, str) |
356 PostMessage(ui, self.name, pmsg, subject=self.Subject()) | 371 PostMessage(ui, self.name, pmsg, subject=self.Subject()) |
357 self.mailed = True | 372 self.mailed = True |
358 self.Flush(ui, repo) | 373 self.Flush(ui, repo) |
359 | 374 |
360 def GoodCLName(name): | 375 def GoodCLName(name): |
361 typecheck(name, str) | 376 typecheck(name, str) |
362 return re.match("^[0-9]+$", name) | 377 return re.match("^[0-9]+$", name) |
(...skipping 712 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1075 | 1090 |
1076 if not opts["stdin"] and not opts["stdout"]: | 1091 if not opts["stdin"] and not opts["stdout"]: |
1077 if name == "new": | 1092 if name == "new": |
1078 cl.files = files | 1093 cl.files = files |
1079 err = EditCL(ui, repo, cl) | 1094 err = EditCL(ui, repo, cl) |
1080 if err != "": | 1095 if err != "": |
1081 return err | 1096 return err |
1082 dirty[cl] = True | 1097 dirty[cl] = True |
1083 | 1098 |
1084 for d, _ in dirty.items(): | 1099 for d, _ in dirty.items(): |
| 1100 name = d.name |
1085 d.Flush(ui, repo) | 1101 d.Flush(ui, repo) |
| 1102 if name == "new": |
| 1103 d.Upload(ui, repo, quiet=True) |
1086 | 1104 |
1087 if opts["stdout"]: | 1105 if opts["stdout"]: |
1088 ui.write(cl.EditorText()) | 1106 ui.write(cl.EditorText()) |
1089 elif name == "new": | 1107 elif name == "new": |
1090 if ui.quiet: | 1108 if ui.quiet: |
1091 ui.write(cl.name) | 1109 ui.write(cl.name) |
1092 else: | 1110 else: |
1093 ui.write("CL created: " + cl.url + "\n") | 1111 ui.write("CL created: " + cl.url + "\n") |
1094 return | 1112 return |
1095 | 1113 |
(...skipping 25 matching lines...) Expand all Loading... |
1121 argv = ["hgpatch"] | 1139 argv = ["hgpatch"] |
1122 if opts["no_incoming"]: | 1140 if opts["no_incoming"]: |
1123 argv += ["--checksync=false"] | 1141 argv += ["--checksync=false"] |
1124 if err != "": | 1142 if err != "": |
1125 return err | 1143 return err |
1126 try: | 1144 try: |
1127 cmd = subprocess.Popen(argv, shell=False, stdin=subprocess.PIPE,
stdout=subprocess.PIPE, stderr=None, close_fds=sys.platform != "win32") | 1145 cmd = subprocess.Popen(argv, shell=False, stdin=subprocess.PIPE,
stdout=subprocess.PIPE, stderr=None, close_fds=sys.platform != "win32") |
1128 except: | 1146 except: |
1129 return "hgpatch: " + ExceptionDetail() | 1147 return "hgpatch: " + ExceptionDetail() |
1130 | 1148 |
1131 » if sys.platform == "win32": | 1149 » cmd.stdin.write(patch) |
1132 » » cmd.stdin.write(patch) | |
1133 » » cmd.stdin.flush() | |
1134 » else: | |
1135 » » if os.fork() == 0: | |
1136 » » » cmd.stdin.write(patch) | |
1137 » » » os._exit(0) | |
1138 cmd.stdin.close() | 1150 cmd.stdin.close() |
1139 out = cmd.stdout.read() | 1151 out = cmd.stdout.read() |
1140 if cmd.wait() != 0 and not opts["ignore_hgpatch_failure"]: | 1152 if cmd.wait() != 0 and not opts["ignore_hgpatch_failure"]: |
1141 return "hgpatch failed" | 1153 return "hgpatch failed" |
1142 cl.local = True | 1154 cl.local = True |
1143 cl.files = out.strip().split() | 1155 cl.files = out.strip().split() |
1144 files = ChangedFiles(ui, repo, [], opts) | 1156 files = ChangedFiles(ui, repo, [], opts) |
1145 extra = Sub(cl.files, files) | 1157 extra = Sub(cl.files, files) |
1146 if extra: | 1158 if extra: |
1147 ui.warn("warning: these files were listed in the patch but not c
hanged:\n\t" + "\n\t".join(extra) + "\n") | 1159 ui.warn("warning: these files were listed in the patch but not c
hanged:\n\t" + "\n\t".join(extra) + "\n") |
(...skipping 1773 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2921 ctype, body = EncodeMultipartFormData(form_fields, files) | 2933 ctype, body = EncodeMultipartFormData(form_fields, files) |
2922 url = "/%d/upload_patch/%d" % (int(issue), int(patchset)) | 2934 url = "/%d/upload_patch/%d" % (int(issue), int(patchset)) |
2923 print "Uploading patch for " + patch[0] | 2935 print "Uploading patch for " + patch[0] |
2924 response_body = rpc_server.Send(url, body, content_type=ctype) | 2936 response_body = rpc_server.Send(url, body, content_type=ctype) |
2925 lines = response_body.splitlines() | 2937 lines = response_body.splitlines() |
2926 if not lines or lines[0] != "OK": | 2938 if not lines or lines[0] != "OK": |
2927 StatusUpdate(" --> %s" % response_body) | 2939 StatusUpdate(" --> %s" % response_body) |
2928 sys.exit(1) | 2940 sys.exit(1) |
2929 rv.append([lines[1], patch[0]]) | 2941 rv.append([lines[1], patch[0]]) |
2930 return rv | 2942 return rv |
LEFT | RIGHT |