Rietveld Code Review Tool
Help | Bug tracker | Discussion group | Source code | Sign in
(438)

Delta Between Two Patch Sets: static/upload.py

Issue 3270: Show images in Rietveld (Closed) Base URL: http://rietveld.googlecode.com/svn/trunk/
Left Patch Set: Fix exception when not upload base file Created 16 years, 7 months ago
Right Patch Set: latest update Created 16 years, 6 months ago
Left:
Right:
Use n/p to move between diff chunks; N/P to move between comments. Please Sign in to add in-line comments.
Jump to:
Left: Side by side diff | Download
Right: Side by side diff | Download
« no previous file with change/comment | « static/blank.jpg ('k') | templates/diff.html » ('j') | no next file with change/comment »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
LEFTRIGHT
1 #!/usr/bin/env python 1 #!/usr/bin/env python
2 # 2 #
3 # Copyright 2007 Google Inc. 3 # Copyright 2007 Google Inc.
4 # 4 #
5 # Licensed under the Apache License, Version 2.0 (the "License"); 5 # Licensed under the Apache License, Version 2.0 (the "License");
6 # you may not use this file except in compliance with the License. 6 # you may not use this file except in compliance with the License.
7 # You may obtain a copy of the License at 7 # You may obtain a copy of the License at
8 # 8 #
9 # http://www.apache.org/licenses/LICENSE-2.0 9 # http://www.apache.org/licenses/LICENSE-2.0
10 # 10 #
(...skipping 139 matching lines...) Expand 10 before | Expand all | Expand 10 after
150 The authentication token returned by ClientLogin. 150 The authentication token returned by ClientLogin.
151 """ 151 """
152 req = self._CreateRequest( 152 req = self._CreateRequest(
153 url="https://www.google.com/accounts/ClientLogin", 153 url="https://www.google.com/accounts/ClientLogin",
154 data=urllib.urlencode({ 154 data=urllib.urlencode({
155 "Email": email, 155 "Email": email,
156 "Passwd": password, 156 "Passwd": password,
157 "service": "ah", 157 "service": "ah",
158 "source": "rietveld-codereview-upload", 158 "source": "rietveld-codereview-upload",
159 "accountType": "GOOGLE", 159 "accountType": "GOOGLE",
160 }) 160 }),
161 ) 161 )
162 try: 162 try:
163 response = self.opener.open(req) 163 response = self.opener.open(req)
164 response_body = response.read() 164 response_body = response.read()
165 response_dict = dict(x.split("=") 165 response_dict = dict(x.split("=")
166 for x in response_body.split("\n") if x) 166 for x in response_body.split("\n") if x)
167 return response_dict["Auth"] 167 return response_dict["Auth"]
168 except urllib2.HTTPError, e: 168 except urllib2.HTTPError, e:
169 if e.code == 403: 169 if e.code == 403:
170 body = e.read() 170 body = e.read()
(...skipping 227 matching lines...) Expand 10 before | Expand all | Expand 10 after
398 help="Add CC (comma separated email addresses).") 398 help="Add CC (comma separated email addresses).")
399 # Upload options 399 # Upload options
400 group = parser.add_option_group("Patch options") 400 group = parser.add_option_group("Patch options")
401 group.add_option("-m", "--message", action="store", dest="message", 401 group.add_option("-m", "--message", action="store", dest="message",
402 metavar="MESSAGE", default=None, 402 metavar="MESSAGE", default=None,
403 help="A message to identify the patch. " 403 help="A message to identify the patch. "
404 "Will prompt if omitted.") 404 "Will prompt if omitted.")
405 group.add_option("-i", "--issue", type="int", action="store", 405 group.add_option("-i", "--issue", type="int", action="store",
406 metavar="ISSUE", default=None, 406 metavar="ISSUE", default=None,
407 help="Issue number to which to add. Defaults to new issue.") 407 help="Issue number to which to add. Defaults to new issue.")
408 group.add_option("-l", "--local_base", action="store_true", 408 group.add_option("--download_base", action="store_true",
409 dest="local_base", default=False, 409 dest="download_base", default=False,
410 help="Base files will be uploaded.") 410 help="Base files will be downloaded by the server "
411 "(side-by-side diffs may not work on files with CRs).")
411 group.add_option("--send_mail", action="store_true", 412 group.add_option("--send_mail", action="store_true",
412 dest="send_mail", default=False, 413 dest="send_mail", default=False,
413 help="Send notification email to reviewers.") 414 help="Send notification email to reviewers.")
414 415
415 416
416 def GetRpcServer(options): 417 def GetRpcServer(options):
417 """Returns an instance of an AbstractRpcServer. 418 """Returns an instance of an AbstractRpcServer.
418 419
419 Returns: 420 Returns:
420 A new AbstractRpcServer, on which RPC calls can be made. 421 A new AbstractRpcServer, on which RPC calls can be made.
421 """ 422 """
422 423
423 rpc_server_class = HttpRpcServer 424 rpc_server_class = HttpRpcServer
424 425
425 def GetUserCredentials(): 426 def GetUserCredentials():
426 """Prompts the user for a username and password.""" 427 """Prompts the user for a username and password."""
427 email = options.email 428 email = options.email
428 if email is None: 429 if email is None:
429 email = raw_input("Email: ").strip() 430 prompt = "Email (login for uploading to %s): " % options.server
431 email = raw_input(prompt).strip()
430 password = getpass.getpass("Password for %s: " % email) 432 password = getpass.getpass("Password for %s: " % email)
431 return (email, password) 433 return (email, password)
432 434
433 # If this is the dev_appserver, use fake authentication. 435 # If this is the dev_appserver, use fake authentication.
434 host = (options.host or options.server).lower() 436 host = (options.host or options.server).lower()
435 if host == "localhost" or host.startswith("localhost:"): 437 if host == "localhost" or host.startswith("localhost:"):
436 email = options.email 438 email = options.email
437 if email is None: 439 if email is None:
438 email = "test@example.com" 440 email = "test@example.com"
439 logging.info("Using debug user %s. Override with --email" % email) 441 logging.info("Using debug user %s. Override with --email" % email)
(...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after
490 492
491 def GetContentType(filename): 493 def GetContentType(filename):
492 """Helper to guess the content-type from the filename.""" 494 """Helper to guess the content-type from the filename."""
493 return mimetypes.guess_type(filename)[0] or 'application/octet-stream' 495 return mimetypes.guess_type(filename)[0] or 'application/octet-stream'
494 496
495 497
496 # Use a shell for subcommands on Windows to get a PATH search. 498 # Use a shell for subcommands on Windows to get a PATH search.
497 use_shell = sys.platform.startswith("win") 499 use_shell = sys.platform.startswith("win")
498 500
499 501
500 def RunShell(command, silent_ok=False, universal_newlines=False): 502 def RunShell(command, silent_ok=False, universal_newlines=True):
501 logging.info("Running %s", command) 503 logging.info("Running %s", command)
502 p = subprocess.Popen(command, stdout=subprocess.PIPE, 504 p = subprocess.Popen(command, stdout=subprocess.PIPE,
503 stderr=subprocess.STDOUT, shell=use_shell, 505 stderr=subprocess.STDOUT, shell=use_shell,
504 universal_newlines=universal_newlines) 506 universal_newlines=universal_newlines)
505 data = p.stdout.read() 507 data = p.stdout.read()
506 p.wait() 508 p.wait()
507 p.stdout.close() 509 p.stdout.close()
508 if p.returncode: 510 if p.returncode:
509 ErrorExit("Got error status from %s" % command) 511 ErrorExit("Got error status from %s" % command)
510 if not silent_ok and not data: 512 if not silent_ok and not data:
(...skipping 29 matching lines...) Expand all
540 answer = raw_input(prompt).strip() 542 answer = raw_input(prompt).strip()
541 if answer != "y": 543 if answer != "y":
542 ErrorExit("User aborted") 544 ErrorExit("User aborted")
543 545
544 def GetBaseFile(self, filename): 546 def GetBaseFile(self, filename):
545 """Get the content of the upstream version of a file. 547 """Get the content of the upstream version of a file.
546 548
547 Returns: 549 Returns:
548 A tuple (base_content, new_content, is_binary, status) 550 A tuple (base_content, new_content, is_binary, status)
549 base_content: The contents of the base file. 551 base_content: The contents of the base file.
550 new_content: For text files, this is empty. For image files, this is 552 new_content: For text files, this is empty. For binary files, this is
GvR 2008/09/07 00:04:18 image -> binary ?
JohnA 2008/09/09 04:06:07 Done (although this is really the same thing, sinc
551 the contents of the new file, since the diff output won't contain 553 the contents of the new file, since the diff output won't contain
552 information to reconstruct the current file. 554 information to reconstruct the current file.
553 is_binary: True iff the file is binary. 555 is_binary: True iff the file is binary.
554 status: The status of the file. 556 status: The status of the file.
555 """ 557 """
556 558
557 raise NotImplementedError( 559 raise NotImplementedError(
558 "abstract method -- subclass %s must override" % self.__class__) 560 "abstract method -- subclass %s must override" % self.__class__)
559 561
560 def UploadBaseFile(self, issue, rpc_server, patch_list, patchset, options,
561 filename, fileno, content, is_binary, status, is_base):
GvR 2008/09/07 00:04:18 Docstring required. Also, isn't there some kind o
JohnA 2008/09/09 04:06:07 Added docstring. Renamed to UploadFile. Made it
562 file_too_large = False
563 if is_base:
564 type = "base"
565 else:
566 type = "current"
567 if len(content) > MAX_UPLOAD_SIZE:
568 print ("Not uploading the %s file for %s because it's too large." %
569 (type, filename))
570 file_too_large = True
571 content = ""
572 checksum = md5.new(content).hexdigest()
573 if options.verbose > 0:
574 print "Uploading %s file for %s" % (type, filename)
GvR 2008/09/07 00:04:18 Isn't this misleading when it's too large?
JohnA 2008/09/09 04:06:07 Done.
575 url = "/%d/upload_content/%d/%d" % (int(issue), int(patchset), fileno)
576 form_fields = [("filename", filename),
577 ("status", status),
578 ("checksum", checksum),
579 ("is_binary", str(is_binary)),
580 ("is_base", str(is_base)),]
GvR 2008/09/07 00:04:18 Move the ']' to a new line.
JohnA 2008/09/09 04:06:07 Done.
581 if file_too_large:
582 form_fields.append(("file_too_large", "1"))
583 if options.email:
GvR 2008/09/07 00:04:18 I'm curious -- does this mean you can upload anony
JohnA 2008/09/09 04:06:07 No, GetUserCredentials will get called after since
584 form_fields.append(("user", options.email))
585 ctype, body = EncodeMultipartFormData(form_fields,
586 [("data", filename, content)])
587 response_body = rpc_server.Send(url, body,
588 content_type=ctype)
589 if not response_body.startswith("OK"):
590 StatusUpdate(" --> %s" % response_body)
591 sys.exit(False)
GvR 2008/09/07 00:04:18 False -> 1. sys.exit() uses 0 for success, nonzer
JohnA 2008/09/09 04:06:07 Changed all places where this occurred in the code
592
593 def UploadBaseFiles(self, issue, rpc_server, patch_list, patchset, options): 562 def UploadBaseFiles(self, issue, rpc_server, patch_list, patchset, options):
594 """Uploads the base files.""" 563 """Uploads the base files (and if necessary, the current ones as well)."""
564
565 def UploadFile(filename, file_id, content, is_binary, status, is_base):
566 """Uploads a file to the server."""
567 file_too_large = False
568 if is_base:
569 type = "base"
570 else:
571 type = "current"
572 if len(content) > MAX_UPLOAD_SIZE:
573 print ("Not uploading the %s file for %s because it's too large." %
574 (type, filename))
575 file_too_large = True
576 content = ""
577 checksum = md5.new(content).hexdigest()
578 if options.verbose > 0 and not file_too_large:
579 print "Uploading %s file for %s" % (type, filename)
580 url = "/%d/upload_content/%d/%d" % (int(issue), int(patchset), file_id)
581 form_fields = [("filename", filename),
582 ("status", status),
583 ("checksum", checksum),
584 ("is_binary", str(is_binary)),
585 ("is_current", str(not is_base)),
586 ]
587 if file_too_large:
588 form_fields.append(("file_too_large", "1"))
589 if options.email:
590 form_fields.append(("user", options.email))
591 ctype, body = EncodeMultipartFormData(form_fields,
592 [("data", filename, content)])
593 response_body = rpc_server.Send(url, body,
594 content_type=ctype)
595 if not response_body.startswith("OK"):
596 StatusUpdate(" --> %s" % response_body)
597 sys.exit(1)
598
595 patches = dict() 599 patches = dict()
596 [patches.setdefault(v, k) for k, v in patch_list] 600 [patches.setdefault(v, k) for k, v in patch_list]
597 for filename in patches.keys(): 601 for filename in patches.keys():
598 fileno = int(patches.get(filename)) 602 file_id = int(patches.get(filename))
GvR 2008/09/07 00:04:18 'fileno' is a terrible name... It makes me thing o
JohnA 2008/09/09 04:06:07 Done.
599 base_content, new_content, is_binary, status = self.GetBaseFile(filename) 603 base_content, new_content, is_binary, status = self.GetBaseFile(filename)
600 if base_content: 604 if base_content:
601 self.UploadBaseFile(issue, rpc_server, patch_list, patchset, options, 605 UploadFile(filename, file_id, base_content, is_binary, status, True)
602 filename, fileno, base_content, is_binary, status,
603 True)
604 if new_content: 606 if new_content:
605 self.UploadBaseFile(issue, rpc_server, patch_list, patchset, options, 607 UploadFile(filename, file_id, new_content, is_binary, status, False)
606 filename, fileno, new_content, is_binary, status,
607 False)
608 608
609 609
610 class SubversionVCS(VersionControlSystem): 610 class SubversionVCS(VersionControlSystem):
611 """Implementation of the VersionControlSystem interface for Subversion.""" 611 """Implementation of the VersionControlSystem interface for Subversion."""
612 612
613 def GuessBase(self, required): 613 def GuessBase(self, required):
614 """Returns the SVN base URL. 614 """Returns the SVN base URL.
615 615
616 Args: 616 Args:
617 required: If true, exits if the url can't be guessed, otherwise None is 617 required: If true, exits if the url can't be guessed, otherwise None is
(...skipping 65 matching lines...) Expand 10 before | Expand all | Expand 10 after
683 'Author': ['Author', 'LastChangedBy'], 683 'Author': ['Author', 'LastChangedBy'],
684 'HeadURL': ['HeadURL', 'URL'], 684 'HeadURL': ['HeadURL', 'URL'],
685 'Id': ['Id'], 685 'Id': ['Id'],
686 686
687 # Aliases 687 # Aliases
688 'LastChangedDate': ['LastChangedDate', 'Date'], 688 'LastChangedDate': ['LastChangedDate', 'Date'],
689 'LastChangedRevision': ['LastChangedRevision', 'Rev', 'Revision'], 689 'LastChangedRevision': ['LastChangedRevision', 'Rev', 'Revision'],
690 'LastChangedBy': ['LastChangedBy', 'Author'], 690 'LastChangedBy': ['LastChangedBy', 'Author'],
691 'URL': ['URL', 'HeadURL'], 691 'URL': ['URL', 'HeadURL'],
692 } 692 }
693
693 def repl(m): 694 def repl(m):
694 if m.group(2): 695 if m.group(2):
695 return "$%s::%s$" % (m.group(1), " " * len(m.group(3))) 696 return "$%s::%s$" % (m.group(1), " " * len(m.group(3)))
696 return "$%s$" % m.group(1) 697 return "$%s$" % m.group(1)
697 keywords = [keyword 698 keywords = [keyword
698 for name in keyword_str.split(" ") 699 for name in keyword_str.split(" ")
699 for keyword in svn_keywords.get(name, [])] 700 for keyword in svn_keywords.get(name, [])]
700 return re.sub(r"\$(%s):(:?)([^\$]+)\$" % '|'.join(keywords), repl, content) 701 return re.sub(r"\$(%s):(:?)([^\$]+)\$" % '|'.join(keywords), repl, content)
701 702
702 def GetUnknownFiles(self): 703 def GetUnknownFiles(self):
703 status = RunShell(["svn", "status", "--ignore-externals"], silent_ok=True) 704 status = RunShell(["svn", "status", "--ignore-externals"], silent_ok=True)
704 unknown_files = [] 705 unknown_files = []
705 for line in status.split("\n"): 706 for line in status.split("\n"):
706 if line and line[0] == "?": 707 if line and line[0] == "?":
707 unknown_files.append(line) 708 unknown_files.append(line)
708 return unknown_files 709 return unknown_files
709 710
710 def IsImage(self, filename): 711 def IsImage(self, filename):
GvR 2008/09/07 00:04:18 Docstring.
711 unused, ext = os.path.splitext(filename) 712 """Returns true if the filename has an image extension."""
712 if not ext: 713 mimetype = mimetypes.guess_type(filename)[0]
714 if not mimetype:
713 return False 715 return False
714 ext = ext[1:].lower() 716 return mimetype.startswith("image/")
715 return ext in ("bmp", "gif", "ico", "jpeg", "jpg", "png", "tif", "tiff")
GvR 2008/09/07 00:04:18 You should be using the mimetypes module instead o
JohnA 2008/09/09 04:06:07 Done (thanks, didn't know about mimetypes).
716 717
717 def ReadFile(self, filename): 718 def ReadFile(self, filename):
718 """Returns the contents of a file.""" 719 """Returns the contents of a file."""
719 file = open(filename, 'rb') 720 file = open(filename, 'rb')
720 result = file.read() 721 result = ""
721 file.close() 722 try:
GvR 2008/09/07 00:04:18 Use this pattern: file = open(...) try: result
JohnA 2008/09/09 04:06:07 Done.
723 result = file.read()
724 finally:
725 file.close()
722 return result 726 return result
723 727
724 def GetBaseFile(self, filename): 728 def GetBaseFile(self, filename):
725 status = RunShell(["svn", "status", "--ignore-externals", filename]) 729 status = RunShell(["svn", "status", "--ignore-externals", filename])
726 if not status: 730 if not status:
727 StatusUpdate("svn status returned no output for %s" % filename) 731 StatusUpdate("svn status returned no output for %s" % filename)
728 sys.exit(False) 732 sys.exit(1)
729 status_lines = status.splitlines() 733 status_lines = status.splitlines()
730 # If file is in a cl, the output will begin with 734 # If file is in a cl, the output will begin with
731 # "\n--- Changelist 'cl_name':\n". See 735 # "\n--- Changelist 'cl_name':\n". See
732 # http://svn.collab.net/repos/svn/trunk/notes/changelist-design.txt 736 # http://svn.collab.net/repos/svn/trunk/notes/changelist-design.txt
733 if (len(status_lines) == 3 and 737 if (len(status_lines) == 3 and
734 not status_lines[0] and 738 not status_lines[0] and
735 status_lines[1].startswith("--- Changelist")): 739 status_lines[1].startswith("--- Changelist")):
736 status = status_lines[2] 740 status = status_lines[2]
737 else: 741 else:
738 status = status_lines[0] 742 status = status_lines[0]
739 base_content = "" 743 base_content = ""
740 new_content = "" 744 new_content = ""
741 745
742 # If a file is copied its status will be "A +", which signifies 746 # If a file is copied its status will be "A +", which signifies
743 # "addition-with-history". See "svn st" for more information. We need to 747 # "addition-with-history". See "svn st" for more information. We need to
744 # upload the original file or else diff parsing will fail if the file was 748 # upload the original file or else diff parsing will fail if the file was
745 # edited. 749 # edited.
746 if (status[0] == "A" and status[3] != "+"): 750 if status[0] == "A" and status[3] != "+":
GvR 2008/09/07 00:04:18 No need to have () around the tests.
JohnA 2008/09/09 04:06:07 Done.
747 # We'll need to upload the new content if we're adding a binary file 751 # We'll need to upload the new content if we're adding a binary file
748 # since diff's output won't contain it. 752 # since diff's output won't contain it.
749 mimetype = RunShell(["svn", "propget", "svn:mime-type", filename], 753 mimetype = RunShell(["svn", "propget", "svn:mime-type", filename],
750 silent_ok=True) 754 silent_ok=True)
751 is_binary = mimetype.startswith("application/octet-stream") 755 is_binary = mimetype and not mimetype.startswith("text/")
GvR 2008/09/07 00:04:18 That seems somewhat restrictive. What if someone w
JohnA 2008/09/09 04:06:07 Good point, done.
752 if is_binary and self.IsImage(filename): 756 if is_binary and self.IsImage(filename):
753 new_content = self.ReadFile(filename) 757 new_content = self.ReadFile(filename)
754 elif status[0] == " " and status[1] == "M": 758 elif status[0] == " " and status[1] == "M":
755 # Property changed, don't need to do anything. 759 # Property changed, don't need to do anything.
756 pass 760 pass
757 elif (status[0] in ("M", "D", "R") or 761 elif (status[0] in ("M", "D", "R") or
758 (status[0] == "A" and status[3] == "+")): 762 (status[0] == "A" and status[3] == "+")):
759 mimetype = RunShell(["svn", "-rBASE", "propget", "svn:mime-type", 763 mimetype = RunShell(["svn", "-rBASE", "propget", "svn:mime-type",
760 filename], 764 filename],
761 silent_ok=True) 765 silent_ok=True)
762 is_binary = mimetype.startswith("application/octet-stream") 766 is_binary = mimetype and not mimetype.startswith("text/")
763 get_base = False 767 get_base = False
764 if not is_binary: 768 if not is_binary:
765 get_base = True 769 get_base = True
766 elif self.IsImage(filename) and status[0] == "M":·········· 770 elif self.IsImage(filename) and status[0] == "M":··········
767 new_content = self.ReadFile(filename) 771 new_content = self.ReadFile(filename)
768 get_base = True 772 get_base = True
769 773
770 if get_base: 774 if get_base:
771 # On Windows svn cat gives \r\n, and calling subprocess.Popen turns 775 if is_binary:
772 # them into \r\r\n, so use universal newlines to avoid the extra \r. 776 universal_newlines = False
773 if sys.platform.startswith("win") and not is_binary:
774 nl = True
775 else: 777 else:
776 nl = False 778 universal_newlines = True
777 base_content = RunShell(["svn", "cat", filename], universal_newlines=nl) 779 base_content = RunShell(["svn", "cat", filename],
780 universal_newlines=universal_newlines)
778 if not is_binary: 781 if not is_binary:
779 keywords = RunShell(["svn", "-rBASE", "propget", "svn:keywords", 782 keywords = RunShell(["svn", "-rBASE", "propget", "svn:keywords",
780 filename], 783 filename],
781 silent_ok=True) 784 silent_ok=True)
782 if keywords: 785 if keywords:
783 base_content = self._CollapseKeywords(base_content, keywords) 786 base_content = self._CollapseKeywords(base_content, keywords)
784 else: 787 else:
785 StatusUpdate("svn status returned unexpected output: %s" % status) 788 StatusUpdate("svn status returned unexpected output: %s" % status)
786 sys.exit(False) 789 sys.exit(1)
GvR 2008/09/07 00:04:18 Again, sys.exit(1). I guess I missed this on an e
JohnA 2008/09/09 04:06:07 Done.
787 return base_content, new_content, is_binary, status[0:5] 790 return base_content, new_content, is_binary, status[0:5]
788 791
789 792
790 class GitVCS(VersionControlSystem): 793 class GitVCS(VersionControlSystem):
791 """Implementation of the VersionControlSystem interface for Git.""" 794 """Implementation of the VersionControlSystem interface for Git."""
792 795
793 def __init__(self): 796 def __init__(self):
794 # Map of filename -> hash of base file. 797 # Map of filename -> hash of base file.
795 self.base_hashes = {} 798 self.base_hashes = {}
796 799
(...skipping 17 matching lines...) Expand all
814 # We want to save the left hash, as that identifies the base file. 817 # We want to save the left hash, as that identifies the base file.
815 match = re.match(r"index (\w+)\.\.", line) 818 match = re.match(r"index (\w+)\.\.", line)
816 if match: 819 if match:
817 self.base_hashes[filename] = match.group(1) 820 self.base_hashes[filename] = match.group(1)
818 svndiff.append(line + "\n") 821 svndiff.append(line + "\n")
819 if not filecount: 822 if not filecount:
820 ErrorExit("No valid patches found in output from git diff") 823 ErrorExit("No valid patches found in output from git diff")
821 return "".join(svndiff) 824 return "".join(svndiff)
822 825
823 def GetUnknownFiles(self): 826 def GetUnknownFiles(self):
824 status = RunShell(["git", "ls-files", "--others"], silent_ok=True) 827 status = RunShell(["git", "ls-files", "--exclude-standard", "--others"],
828 silent_ok=True)
825 return status.splitlines() 829 return status.splitlines()
826 830
827 def GetBaseFile(self, filename): 831 def GetBaseFile(self, filename):
828 hash = self.base_hashes[filename] 832 hash = self.base_hashes[filename]
833 base_content = ""
834 new_content = ""
835 is_binary = False
829 if hash == "0" * 40: # All-zero hash indicates no base file. 836 if hash == "0" * 40: # All-zero hash indicates no base file.
830 return ("", "", False, "A") 837 status = "A"
831 else: 838 else:
832 return (RunShell(["git", "show", hash]), "", False, "M") 839 status = "M"
GvR 2008/09/07 00:04:18 Mind putting the RunShell() return value into a te
JohnA 2008/09/09 04:06:07 Done.
833 840 base_content = RunShell(["git", "show", hash])
834 841 return (base_content, new_content, is_binary, status)
835 # NOTE: this function is duplicated in engine.py, keep them in sync. 842
843
844 # NOTE: The SplitPatch function is duplicated in engine.py, keep them in sync.
836 def SplitPatch(data): 845 def SplitPatch(data):
837 """Splits a patch into separate pieces for each file. 846 """Splits a patch into separate pieces for each file.
838 847
839 Args: 848 Args:
840 data: A string containing the output of svn diff. 849 data: A string containing the output of svn diff.
841 850
842 Returns: 851 Returns:
843 A list of 2-tuple (filename, text) where text is the svn diff output 852 A list of 2-tuple (filename, text) where text is the svn diff output
844 pertaining to filename. 853 pertaining to filename.
845 """ 854 """
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after
879 Returns a list of [patch_key, filename] for each file. 888 Returns a list of [patch_key, filename] for each file.
880 """ 889 """
881 patches = SplitPatch(data) 890 patches = SplitPatch(data)
882 rv = [] 891 rv = []
883 for patch in patches: 892 for patch in patches:
884 if len(patch[1]) > MAX_UPLOAD_SIZE: 893 if len(patch[1]) > MAX_UPLOAD_SIZE:
885 print ("Not uploading the patch for " + patch[0] + 894 print ("Not uploading the patch for " + patch[0] +
886 " because the file is too large.") 895 " because the file is too large.")
887 continue 896 continue
888 form_fields = [("filename", patch[0])] 897 form_fields = [("filename", patch[0])]
889 if options.local_base: 898 if not options.download_base:
890 form_fields.append(("content_upload", "1")) 899 form_fields.append(("content_upload", "1"))
891 files = [("data", "data.diff", patch[1])] 900 files = [("data", "data.diff", patch[1])]
892 ctype, body = EncodeMultipartFormData(form_fields, files) 901 ctype, body = EncodeMultipartFormData(form_fields, files)
893 url = "/%d/upload_patch/%d" % (int(issue), int(patchset)) 902 url = "/%d/upload_patch/%d" % (int(issue), int(patchset))
894 print "Uploading patch for " + patch[0] 903 print "Uploading patch for " + patch[0]
895 response_body = rpc_server.Send(url, body, content_type=ctype) 904 response_body = rpc_server.Send(url, body, content_type=ctype)
896 lines = response_body.splitlines() 905 lines = response_body.splitlines()
897 if not lines or lines[0] != "OK": 906 if not lines or lines[0] != "OK":
898 StatusUpdate(" --> %s" % response_body) 907 StatusUpdate(" --> %s" % response_body)
899 sys.exit(False) 908 sys.exit(1)
900 rv.append([lines[1], patch[0]]) 909 rv.append([lines[1], patch[0]])
901 return rv 910 return rv
902 911
903 912
904 def GuessVCS(): 913 def GuessVCS():
905 """Helper to guess the version control system. 914 """Helper to guess the version control system.
906 915
907 This examines the current directory, guesses which VersionControlSystem 916 This examines the current directory, guesses which VersionControlSystem
908 we're using, and returns an instance of the appropriate class. Exit with an 917 we're using, and returns an instance of the appropriate class. Exit with an
909 error if we can't figure it out. 918 error if we can't figure it out.
(...skipping 29 matching lines...) Expand all
939 global verbosity 948 global verbosity
940 verbosity = options.verbose 949 verbosity = options.verbose
941 if verbosity >= 3: 950 if verbosity >= 3:
942 logging.getLogger().setLevel(logging.DEBUG) 951 logging.getLogger().setLevel(logging.DEBUG)
943 elif verbosity >= 2: 952 elif verbosity >= 2:
944 logging.getLogger().setLevel(logging.INFO) 953 logging.getLogger().setLevel(logging.INFO)
945 vcs = GuessVCS() 954 vcs = GuessVCS()
946 if isinstance(vcs, SubversionVCS): 955 if isinstance(vcs, SubversionVCS):
947 # base field is only allowed for Subversion. 956 # base field is only allowed for Subversion.
948 # Note: Fetching base files may become deprecated in future releases. 957 # Note: Fetching base files may become deprecated in future releases.
949 base = vcs.GuessBase(not options.local_base) 958 base = vcs.GuessBase(options.download_base)
950 else: 959 else:
951 base = None 960 base = None
952 if not base and not options.local_base: 961 if not base and options.download_base:
953 options.local_base = True 962 options.download_base = True
954 logging.info("Enabled upload of base file") 963 logging.info("Enabled upload of base file")
955 if not options.assume_yes: 964 if not options.assume_yes:
956 vcs.CheckForUnknownFiles() 965 vcs.CheckForUnknownFiles()
957 if data is None: 966 if data is None:
958 data = vcs.GenerateDiff(args) 967 data = vcs.GenerateDiff(args)
959 if verbosity >= 1: 968 if verbosity >= 1:
960 print "Upload server:", options.server, "(change with -s/--server)" 969 print "Upload server:", options.server, "(change with -s/--server)"
961 if options.issue: 970 if options.issue:
962 prompt = "Message describing this patch set: " 971 prompt = "Message describing this patch set: "
963 else: 972 else:
(...skipping 23 matching lines...) Expand all
987 if options.description_file: 996 if options.description_file:
988 if options.description: 997 if options.description:
989 ErrorExit("Can't specify description and description_file") 998 ErrorExit("Can't specify description and description_file")
990 file = open(options.description_file, 'r') 999 file = open(options.description_file, 'r')
991 description = file.read() 1000 description = file.read()
992 file.close() 1001 file.close()
993 if description: 1002 if description:
994 form_fields.append(("description", description)) 1003 form_fields.append(("description", description))
995 # If we're uploading base files, don't send the email before the uploads, so 1004 # If we're uploading base files, don't send the email before the uploads, so
996 # that it contains the file status. 1005 # that it contains the file status.
997 if options.send_mail and not options.local_base: 1006 if options.send_mail and options.download_base:
998 form_fields.append(("send_mail", "1")) 1007 form_fields.append(("send_mail", "1"))
999 if options.local_base: 1008 if not options.download_base:
1000 form_fields.append(("content_upload", "1")) 1009 form_fields.append(("content_upload", "1"))
1001 if len(data) > MAX_UPLOAD_SIZE: 1010 if len(data) > MAX_UPLOAD_SIZE:
1002 print "Patch is large, so uploading file patches separately." 1011 print "Patch is large, so uploading file patches separately."
1003 files = [] 1012 files = []
1004 form_fields.append(("separate_patches", "1")) 1013 form_fields.append(("separate_patches", "1"))
1005 else: 1014 else:
1006 files = [("data", "data.diff", data)] 1015 files = [("data", "data.diff", data)]
1007 ctype, body = EncodeMultipartFormData(form_fields, files) 1016 ctype, body = EncodeMultipartFormData(form_fields, files)
1008 response_body = rpc_server.Send("/upload", body, content_type=ctype) 1017 response_body = rpc_server.Send("/upload", body, content_type=ctype)
1009 if options.local_base or not files: 1018 if not options.download_base or not files:
1010 lines = response_body.splitlines() 1019 lines = response_body.splitlines()
1011 if len(lines) >= 2: 1020 if len(lines) >= 2:
1012 msg = lines[0] 1021 msg = lines[0]
1013 patchset = lines[1].strip() 1022 patchset = lines[1].strip()
1014 patches = [x.split(" ", 1) for x in lines[2:]] 1023 patches = [x.split(" ", 1) for x in lines[2:]]
1015 else: 1024 else:
1016 msg = response_body 1025 msg = response_body
1017 else: 1026 else:
1018 msg = response_body 1027 msg = response_body
1019 StatusUpdate(msg) 1028 StatusUpdate(msg)
1020 if not response_body.startswith("Issue created.") and \ 1029 if not response_body.startswith("Issue created.") and \
1021 not response_body.startswith("Issue updated."): 1030 not response_body.startswith("Issue updated."):
1022 sys.exit(0) 1031 sys.exit(0)
1023 issue = msg[msg.rfind("/")+1:] 1032 issue = msg[msg.rfind("/")+1:]
1024 1033
1025 if not files: 1034 if not files:
1026 result = UploadSeparatePatches(issue, rpc_server, patchset, data, options) 1035 result = UploadSeparatePatches(issue, rpc_server, patchset, data, options)
1027 if options.local_base: 1036 if not options.download_base:
1028 patches = result 1037 patches = result
1029 1038
1030 if options.local_base: 1039 if not options.download_base:
1031 vcs.UploadBaseFiles(issue, rpc_server, patches, patchset, options) 1040 vcs.UploadBaseFiles(issue, rpc_server, patches, patchset, options)
1032 if options.send_mail: 1041 if options.send_mail:
1033 rpc_server.Send("/" + issue + "/mail") 1042 rpc_server.Send("/" + issue + "/mail", payload="")
1034 return issue 1043 return issue
1035 1044
1036 1045
1037 def main(): 1046 def main():
1038 try: 1047 try:
1039 RealMain(sys.argv) 1048 RealMain(sys.argv)
1040 except KeyboardInterrupt: 1049 except KeyboardInterrupt:
1041 print 1050 print
1042 StatusUpdate("Interrupted.") 1051 StatusUpdate("Interrupted.")
1043 sys.exit(1) 1052 sys.exit(1)
1044 1053
1045 1054
1046 if __name__ == "__main__": 1055 if __name__ == "__main__":
1047 main() 1056 main()
LEFTRIGHT

Powered by Google App Engine
RSS Feeds Recent Issues | This issue
This is Rietveld f62528b