Left: | ||
Right: |
LEFT | RIGHT |
---|---|
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 Loading... | |
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 Loading... | |
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 Loading... | |
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 Loading... | |
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 Loading... | |
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 Loading... | |
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 Loading... | |
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 Loading... | |
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 Loading... | |
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() |
LEFT | RIGHT |