Update upload.py to revision 488 and remove HOSTED support.

TEST=not tested at all.

Review URL: http://codereview.chromium.org/414035

git-svn-id: svn://svn.chromium.org/chrome/trunk/tools/depot_tools@32611 0039d316-1c4b-4281-b951-d872f2087c98
experimental/szager/collated-output
maruel@chromium.org 16 years ago
parent b9f2f6236b
commit b563dd39e6

@ -45,13 +45,11 @@ import urllib
import urllib2 import urllib2
import urlparse import urlparse
# Work-around for md5 module deprecation warning in python 2.5+: # The md5 module was deprecated in Python 2.5.
try: try:
# Try to load hashlib (python 2.5+)
from hashlib import md5 from hashlib import md5
except ImportError: except ImportError:
# If hashlib cannot be imported, load md5.new instead. from md5 import md5
from md5 import new as md5
try: try:
import readline import readline
@ -68,8 +66,27 @@ verbosity = 1
# Max size of patch or base file. # Max size of patch or base file.
MAX_UPLOAD_SIZE = 900 * 1024 MAX_UPLOAD_SIZE = 900 * 1024
# Constants for version control names. Used by GuessVCSName.
VCS_GIT = "Git"
VCS_MERCURIAL = "Mercurial"
VCS_SUBVERSION = "Subversion"
VCS_UNKNOWN = "Unknown"
# whitelist for non-binary filetypes which do not start with "text/"
# .mm (Objective-C) shows up as application/x-freemind on my Linux box.
TEXT_MIMETYPES = ['application/javascript', 'application/x-javascript',
'application/x-freemind']
VCS_ABBREVIATIONS = {
VCS_MERCURIAL.lower(): VCS_MERCURIAL,
"hg": VCS_MERCURIAL,
VCS_SUBVERSION.lower(): VCS_SUBVERSION,
"svn": VCS_SUBVERSION,
VCS_GIT.lower(): VCS_GIT,
}
def GetEmail(): def GetEmail(prompt):
"""Prompts the user for their email address and returns it. """Prompts the user for their email address and returns it.
The last used email address is saved to a file and offered up as a suggestion The last used email address is saved to a file and offered up as a suggestion
@ -78,19 +95,17 @@ def GetEmail():
for next time we prompt. for next time we prompt.
""" """
last_email_file_name = os.path.expanduser( last_email_file_name = os.path.expanduser("~/.last_codereview_email_address")
os.path.join("~", ".last_codereview_email_address"))
last_email = "" last_email = ""
prompt = "Email: "
if os.path.exists(last_email_file_name): if os.path.exists(last_email_file_name):
try: try:
last_email_file = open(last_email_file_name, "r") last_email_file = open(last_email_file_name, "r")
last_email = last_email_file.readline().strip("\n") last_email = last_email_file.readline().strip("\n")
last_email_file.close() last_email_file.close()
prompt = "Email [%s]: " % last_email prompt += " [%s]" % last_email
except IOError, e: except IOError, e:
pass pass
email = raw_input(prompt).strip() email = raw_input(prompt + ": ").strip()
if email: if email:
try: try:
last_email_file = open(last_email_file_name, "w") last_email_file = open(last_email_file_name, "w")
@ -193,9 +208,6 @@ class AbstractRpcServer(object):
The authentication token returned by ClientLogin. The authentication token returned by ClientLogin.
""" """
account_type = "GOOGLE" account_type = "GOOGLE"
if self.host.endswith(".google.com"):
# Needed for use inside Google.
account_type = "HOSTED"
req = self._CreateRequest( req = self._CreateRequest(
url="https://www.google.com/accounts/ClientLogin", url="https://www.google.com/accounts/ClientLogin",
data=urllib.urlencode({ data=urllib.urlencode({
@ -257,8 +269,8 @@ class AbstractRpcServer(object):
us to the URL we provided. us to the URL we provided.
If we attempt to access the upload API without first obtaining an If we attempt to access the upload API without first obtaining an
authentication cookie, it returns a 401 response and directs us to authentication cookie, it returns a 401 response (or a 302) and
authenticate ourselves with ClientLogin. directs us to authenticate ourselves with ClientLogin.
""" """
for i in range(3): for i in range(3):
credentials = self.auth_function() credentials = self.auth_function()
@ -339,7 +351,7 @@ class AbstractRpcServer(object):
except urllib2.HTTPError, e: except urllib2.HTTPError, e:
if tries > 3: if tries > 3:
raise raise
elif e.code == 401: elif e.code == 401 or e.code == 302:
self._Authenticate() self._Authenticate()
## elif e.code >= 500 and e.code < 600: ## elif e.code >= 500 and e.code < 600:
## # Server Error - try again. ## # Server Error - try again.
@ -374,8 +386,7 @@ class HttpRpcServer(AbstractRpcServer):
opener.add_handler(urllib2.HTTPSHandler()) opener.add_handler(urllib2.HTTPSHandler())
opener.add_handler(urllib2.HTTPErrorProcessor()) opener.add_handler(urllib2.HTTPErrorProcessor())
if self.save_cookies: if self.save_cookies:
self.cookie_file = os.path.expanduser( self.cookie_file = os.path.expanduser("~/.codereview_upload_cookies")
os.path.join("~", ".codereview_upload_cookies"))
self.cookie_jar = cookielib.MozillaCookieJar(self.cookie_file) self.cookie_jar = cookielib.MozillaCookieJar(self.cookie_file)
if os.path.exists(self.cookie_file): if os.path.exists(self.cookie_file):
try: try:
@ -418,7 +429,7 @@ group.add_option("-s", "--server", action="store", dest="server",
default="codereview.appspot.com", default="codereview.appspot.com",
metavar="SERVER", metavar="SERVER",
help=("The server to upload to. The format is host[:port]. " help=("The server to upload to. The format is host[:port]. "
"Defaults to 'codereview.appspot.com'.")) "Defaults to '%default'."))
group.add_option("-e", "--email", action="store", dest="email", group.add_option("-e", "--email", action="store", dest="email",
metavar="EMAIL", default=None, metavar="EMAIL", default=None,
help="The username to use. Will prompt if omitted.") help="The username to use. Will prompt if omitted.")
@ -444,6 +455,9 @@ group.add_option("-r", "--reviewers", action="store", dest="reviewers",
group.add_option("--cc", action="store", dest="cc", group.add_option("--cc", action="store", dest="cc",
metavar="CC", default=None, metavar="CC", default=None,
help="Add CC (comma separated email addresses).") help="Add CC (comma separated email addresses).")
group.add_option("--private", action="store_true", dest="private",
default=False,
help="Make the issue restricted to reviewers and those CCed")
# Upload options # Upload options
group = parser.add_option_group("Patch options") group = parser.add_option_group("Patch options")
group.add_option("-m", "--message", action="store", dest="message", group.add_option("-m", "--message", action="store", dest="message",
@ -463,6 +477,10 @@ group.add_option("--rev", action="store", dest="revision",
group.add_option("--send_mail", action="store_true", group.add_option("--send_mail", action="store_true",
dest="send_mail", default=False, dest="send_mail", default=False,
help="Send notification email to reviewers.") help="Send notification email to reviewers.")
group.add_option("--vcs", action="store", dest="vcs",
metavar="VCS", default=None,
help=("Version control system (optional, usually upload.py "
"already guesses the right VCS)."))
def GetRpcServer(options): def GetRpcServer(options):
@ -478,7 +496,7 @@ def GetRpcServer(options):
"""Prompts the user for a username and password.""" """Prompts the user for a username and password."""
email = options.email email = options.email
if email is None: if email is None:
email = GetEmail() email = GetEmail("Email (login for uploading to %s)" % options.server)
password = getpass.getpass("Password for %s: " % email) password = getpass.getpass("Password for %s: " % email)
return (email, password) return (email, password)
@ -549,7 +567,8 @@ def GetContentType(filename):
use_shell = sys.platform.startswith("win") use_shell = sys.platform.startswith("win")
def RunShellWithReturnCode(command, print_output=False, def RunShellWithReturnCode(command, print_output=False,
universal_newlines=True): universal_newlines=True,
env=os.environ):
"""Executes a command and returns the output from stdout and the return code. """Executes a command and returns the output from stdout and the return code.
Args: Args:
@ -563,7 +582,8 @@ def RunShellWithReturnCode(command, print_output=False,
""" """
logging.info("Running %s", command) logging.info("Running %s", command)
p = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, p = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE,
shell=use_shell, universal_newlines=universal_newlines) shell=use_shell, universal_newlines=universal_newlines,
env=env)
if print_output: if print_output:
output_array = [] output_array = []
while True: while True:
@ -585,9 +605,9 @@ def RunShellWithReturnCode(command, print_output=False,
def RunShell(command, silent_ok=False, universal_newlines=True, def RunShell(command, silent_ok=False, universal_newlines=True,
print_output=False): print_output=False, env=os.environ):
data, retcode = RunShellWithReturnCode(command, print_output, data, retcode = RunShellWithReturnCode(command, print_output,
universal_newlines) universal_newlines, env)
if retcode: if retcode:
ErrorExit("Got error status from %s:\n%s" % (command, data)) ErrorExit("Got error status from %s:\n%s" % (command, data))
if not silent_ok and not data: if not silent_ok and not data:
@ -727,6 +747,16 @@ class VersionControlSystem(object):
return False return False
return mimetype.startswith("image/") return mimetype.startswith("image/")
def IsBinary(self, filename):
"""Returns true if the guessed mimetyped isnt't in text group."""
mimetype = mimetypes.guess_type(filename)[0]
if not mimetype:
return False # e.g. README, "real" binaries usually have an extension
# special case for text files which don't start with text/
if mimetype in TEXT_MIMETYPES:
return False
return not mimetype.startswith("text/")
class SubversionVCS(VersionControlSystem): class SubversionVCS(VersionControlSystem):
"""Implementation of the VersionControlSystem interface for Subversion.""" """Implementation of the VersionControlSystem interface for Subversion."""
@ -920,7 +950,7 @@ class SubversionVCS(VersionControlSystem):
mimetype = RunShell(["svn", "propget", "svn:mime-type", filename], mimetype = RunShell(["svn", "propget", "svn:mime-type", filename],
silent_ok=True) silent_ok=True)
base_content = "" base_content = ""
is_binary = mimetype and not mimetype.startswith("text/") is_binary = bool(mimetype) and not mimetype.startswith("text/")
if is_binary and self.IsImage(filename): if is_binary and self.IsImage(filename):
new_content = self.ReadFile(filename) new_content = self.ReadFile(filename)
elif (status[0] in ("M", "D", "R") or elif (status[0] in ("M", "D", "R") or
@ -940,7 +970,7 @@ class SubversionVCS(VersionControlSystem):
# Reset mimetype, it contains an error message. # Reset mimetype, it contains an error message.
mimetype = "" mimetype = ""
get_base = False get_base = False
is_binary = mimetype and not mimetype.startswith("text/") is_binary = bool(mimetype) and not mimetype.startswith("text/")
if status[0] == " ": if status[0] == " ":
# Empty base content just to force an upload. # Empty base content just to force an upload.
base_content = "" base_content = ""
@ -997,33 +1027,55 @@ class GitVCS(VersionControlSystem):
def __init__(self, options): def __init__(self, options):
super(GitVCS, self).__init__(options) super(GitVCS, self).__init__(options)
# Map of filename -> hash of base file. # Map of filename -> (hash before, hash after) of base file.
self.base_hashes = {} # Hashes for "no such file" are represented as None.
self.hashes = {}
# Map of new filename -> old filename for renames.
self.renames = {}
def GenerateDiff(self, extra_args): def GenerateDiff(self, extra_args):
# This is more complicated than svn's GenerateDiff because we must convert # This is more complicated than svn's GenerateDiff because we must convert
# the diff output to include an svn-style "Index:" line as well as record # the diff output to include an svn-style "Index:" line as well as record
# the hashes of the base files, so we can upload them along with our diff. # the hashes of the files, so we can upload them along with our diff.
# Special used by git to indicate "no such content".
NULL_HASH = "0"*40
extra_args = extra_args[:]
if self.options.revision: if self.options.revision:
extra_args = [self.options.revision] + extra_args extra_args = [self.options.revision] + extra_args
gitdiff = RunShell(["git", "diff", "--no-ext-diff", "--full-index"] +
extra_args) # --no-ext-diff is broken in some versions of Git, so try to work around
# this by overriding the environment (but there is still a problem if the
# git config key "diff.external" is used).
env = os.environ.copy()
if 'GIT_EXTERNAL_DIFF' in env: del env['GIT_EXTERNAL_DIFF']
gitdiff = RunShell(["git", "diff", "--no-ext-diff", "--full-index", "-M"]
+ extra_args, env=env)
svndiff = [] svndiff = []
filecount = 0 filecount = 0
filename = None filename = None
for line in gitdiff.splitlines(): for line in gitdiff.splitlines():
match = re.match(r"diff --git a/(.*) b/.*$", line) match = re.match(r"diff --git a/(.*) b/(.*)$", line)
if match: if match:
filecount += 1 filecount += 1
filename = match.group(1) # Intentionally use the "after" filename so we can show renames.
filename = match.group(2)
svndiff.append("Index: %s\n" % filename) svndiff.append("Index: %s\n" % filename)
if match.group(1) != match.group(2):
self.renames[match.group(2)] = match.group(1)
else: else:
# The "index" line in a git diff looks like this (long hashes elided): # The "index" line in a git diff looks like this (long hashes elided):
# index 82c0d44..b2cee3f 100755 # index 82c0d44..b2cee3f 100755
# We want to save the left hash, as that identifies the base file. # We want to save the left hash, as that identifies the base file.
match = re.match(r"index (\w+)\.\.", line) match = re.match(r"index (\w+)\.\.(\w+)", line)
if match: if match:
self.base_hashes[filename] = match.group(1) before, after = (match.group(1), match.group(2))
if before == NULL_HASH:
before = None
if after == NULL_HASH:
after = None
self.hashes[filename] = (before, after)
svndiff.append(line + "\n") svndiff.append(line + "\n")
if not filecount: if not filecount:
ErrorExit("No valid patches found in output from git diff") ErrorExit("No valid patches found in output from git diff")
@ -1034,17 +1086,47 @@ class GitVCS(VersionControlSystem):
silent_ok=True) silent_ok=True)
return status.splitlines() return status.splitlines()
def GetFileContent(self, file_hash, is_binary):
"""Returns the content of a file identified by its git hash."""
data, retcode = RunShellWithReturnCode(["git", "show", file_hash],
universal_newlines=not is_binary)
if retcode:
ErrorExit("Got error status from 'git show %s'" % file_hash)
return data
def GetBaseFile(self, filename): def GetBaseFile(self, filename):
hash = self.base_hashes[filename] hash_before, hash_after = self.hashes.get(filename, (None,None))
base_content = None base_content = None
new_content = None new_content = None
is_binary = False is_binary = self.IsBinary(filename)
if hash == "0" * 40: # All-zero hash indicates no base file. status = None
if filename in self.renames:
status = "A +" # Match svn attribute name for renames.
if filename not in self.hashes:
# If a rename doesn't change the content, we never get a hash.
base_content = RunShell(["git", "show", filename])
elif not hash_before:
status = "A" status = "A"
base_content = "" base_content = ""
elif not hash_after:
status = "D"
else: else:
status = "M" status = "M"
base_content = RunShell(["git", "show", hash])
is_image = self.IsImage(filename)
# Grab the before/after content if we need it.
# We should include file contents if it's text or it's an image.
if not is_binary or is_image:
# Grab the base content if we don't have it already.
if base_content is None and hash_before:
base_content = self.GetFileContent(hash_before, is_binary)
# Only include the "after" file if it's an image; otherwise it
# it is reconstructed from the diff.
if is_image and hash_after:
new_content = self.GetFileContent(hash_after, is_binary)
return (base_content, new_content, is_binary, status) return (base_content, new_content, is_binary, status)
@ -1067,7 +1149,7 @@ class MercurialVCS(VersionControlSystem):
def _GetRelPath(self, filename): def _GetRelPath(self, filename):
"""Get relative path of a file according to the current directory, """Get relative path of a file according to the current directory,
given its logical path in the repo.""" given its logical path in the repo."""
assert filename.startswith(self.subdir), filename assert filename.startswith(self.subdir), (filename, self.subdir)
return filename[len(self.subdir):].lstrip(r"\/") return filename[len(self.subdir):].lstrip(r"\/")
def GenerateDiff(self, extra_args): def GenerateDiff(self, extra_args):
@ -1130,8 +1212,12 @@ class MercurialVCS(VersionControlSystem):
status = "M" status = "M"
else: else:
status, _ = out[0].split(' ', 1) status, _ = out[0].split(' ', 1)
if ":" in self.base_rev:
base_rev = self.base_rev.split(":", 1)[0]
else:
base_rev = self.base_rev
if status != "A": if status != "A":
base_content = RunShell(["hg", "cat", "-r", self.base_rev, oldrelpath], base_content = RunShell(["hg", "cat", "-r", base_rev, oldrelpath],
silent_ok=True) silent_ok=True)
is_binary = "\0" in base_content # Mercurial's heuristic is_binary = "\0" in base_content # Mercurial's heuristic
if status != "R": if status != "R":
@ -1139,7 +1225,7 @@ class MercurialVCS(VersionControlSystem):
is_binary = is_binary or "\0" in new_content is_binary = is_binary or "\0" in new_content
if is_binary and base_content: if is_binary and base_content:
# Fetch again without converting newlines # Fetch again without converting newlines
base_content = RunShell(["hg", "cat", "-r", self.base_rev, oldrelpath], base_content = RunShell(["hg", "cat", "-r", base_rev, oldrelpath],
silent_ok=True, universal_newlines=False) silent_ok=True, universal_newlines=False)
if not is_binary or not self.IsImage(relpath): if not is_binary or not self.IsImage(relpath):
new_content = None new_content = None
@ -1215,15 +1301,17 @@ def UploadSeparatePatches(issue, rpc_server, patchset, data, options):
return rv return rv
def GuessVCS(options): def GuessVCSName():
"""Helper to guess the version control system. """Helper to guess the version control system.
This examines the current directory, guesses which VersionControlSystem This examines the current directory, guesses which VersionControlSystem
we're using, and returns an instance of the appropriate class. Exit with an we're using, and returns an string indicating which VCS is detected.
error if we can't figure it out.
Returns: Returns:
A VersionControlSystem instance. Exits if the VCS can't be guessed. A pair (vcs, output). vcs is a string indicating which VCS was detected
and is one of VCS_GIT, VCS_MERCURIAL, VCS_SUBVERSION, or VCS_UNKNOWN.
output is a string containing any interesting output from the vcs
detection routine, or None if there is nothing interesting.
""" """
# Mercurial has a command to get the base directory of a repository # Mercurial has a command to get the base directory of a repository
# Try running it, but don't die if we don't have hg installed. # Try running it, but don't die if we don't have hg installed.
@ -1231,7 +1319,7 @@ def GuessVCS(options):
try: try:
out, returncode = RunShellWithReturnCode(["hg", "root"]) out, returncode = RunShellWithReturnCode(["hg", "root"])
if returncode == 0: if returncode == 0:
return MercurialVCS(options, out.strip()) return (VCS_MERCURIAL, out.strip())
except OSError, (errno, message): except OSError, (errno, message):
if errno != 2: # ENOENT -- they don't have hg installed. if errno != 2: # ENOENT -- they don't have hg installed.
raise raise
@ -1239,7 +1327,7 @@ def GuessVCS(options):
# Subversion has a .svn in all working directories. # Subversion has a .svn in all working directories.
if os.path.isdir('.svn'): if os.path.isdir('.svn'):
logging.info("Guessed VCS = Subversion") logging.info("Guessed VCS = Subversion")
return SubversionVCS(options) return (VCS_SUBVERSION, None)
# Git has a command to test if you're in a git tree. # Git has a command to test if you're in a git tree.
# Try running it, but don't die if we don't have git installed. # Try running it, but don't die if we don't have git installed.
@ -1247,16 +1335,81 @@ def GuessVCS(options):
out, returncode = RunShellWithReturnCode(["git", "rev-parse", out, returncode = RunShellWithReturnCode(["git", "rev-parse",
"--is-inside-work-tree"]) "--is-inside-work-tree"])
if returncode == 0: if returncode == 0:
return GitVCS(options) return (VCS_GIT, None)
except OSError, (errno, message): except OSError, (errno, message):
if errno != 2: # ENOENT -- they don't have git installed. if errno != 2: # ENOENT -- they don't have git installed.
raise raise
return (VCS_UNKNOWN, None)
def GuessVCS(options):
"""Helper to guess the version control system.
This verifies any user-specified VersionControlSystem (by command line
or environment variable). If the user didn't specify one, this examines
the current directory, guesses which VersionControlSystem we're using,
and returns an instance of the appropriate class. Exit with an error
if we can't figure it out.
Returns:
A VersionControlSystem instance. Exits if the VCS can't be guessed.
"""
vcs = options.vcs
if not vcs:
vcs = os.environ.get("CODEREVIEW_VCS")
if vcs:
v = VCS_ABBREVIATIONS.get(vcs.lower())
if v is None:
ErrorExit("Unknown version control system %r specified." % vcs)
(vcs, extra_output) = (v, None)
else:
(vcs, extra_output) = GuessVCSName()
if vcs == VCS_MERCURIAL:
if extra_output is None:
extra_output = RunShell(["hg", "root"]).strip()
return MercurialVCS(options, extra_output)
elif vcs == VCS_SUBVERSION:
return SubversionVCS(options)
elif vcs == VCS_GIT:
return GitVCS(options)
ErrorExit(("Could not guess version control system. " ErrorExit(("Could not guess version control system. "
"Are you in a working copy directory?")) "Are you in a working copy directory?"))
def CheckReviewer(reviewer):
"""Validate a reviewer -- either a nickname or an email addres.
Args:
reviewer: A nickname or an email address.
Calls ErrorExit() if it is an invalid email address.
"""
if "@" not in reviewer:
return # Assume nickname
parts = reviewer.split("@")
if len(parts) > 2:
ErrorExit("Invalid email address: %r" % reviewer)
assert len(parts) == 2
if "." not in parts[1]:
ErrorExit("Invalid email address: %r" % reviewer)
def RealMain(argv, data=None): def RealMain(argv, data=None):
"""The real main function.
Args:
argv: Command line arguments.
data: Diff contents. If None (default) the diff is generated by
the VersionControlSystem implementation returned by GuessVCS().
Returns:
A 2-tuple (issue id, patchset id).
The patchset id is None if the base files are not uploaded by this
script (applies only to SVN checkouts).
"""
logging.basicConfig(format=("%(asctime).19s %(levelname)s %(filename)s:" logging.basicConfig(format=("%(asctime).19s %(levelname)s %(filename)s:"
"%(lineno)s %(message)s ")) "%(lineno)s %(message)s "))
os.environ['LC_ALL'] = 'C' os.environ['LC_ALL'] = 'C'
@ -1301,13 +1454,11 @@ def RealMain(argv, data=None):
form_fields.append(("user", options.email)) form_fields.append(("user", options.email))
if options.reviewers: if options.reviewers:
for reviewer in options.reviewers.split(','): for reviewer in options.reviewers.split(','):
if "@" in reviewer and not reviewer.split("@")[1].count(".") == 1: CheckReviewer(reviewer)
ErrorExit("Invalid email address: %s" % reviewer)
form_fields.append(("reviewers", options.reviewers)) form_fields.append(("reviewers", options.reviewers))
if options.cc: if options.cc:
for cc in options.cc.split(','): for cc in options.cc.split(','):
if "@" in cc and not cc.split("@")[1].count(".") == 1: CheckReviewer(cc)
ErrorExit("Invalid email address: %s" % cc)
form_fields.append(("cc", options.cc)) form_fields.append(("cc", options.cc))
description = options.description description = options.description
if options.description_file: if options.description_file:
@ -1328,6 +1479,11 @@ def RealMain(argv, data=None):
base_hashes += "|" base_hashes += "|"
base_hashes += checksum + ":" + file base_hashes += checksum + ":" + file
form_fields.append(("base_hashes", base_hashes)) form_fields.append(("base_hashes", base_hashes))
if options.private:
if options.issue:
print "Warning: Private flag ignored when updating an existing issue."
else:
form_fields.append(("private", "1"))
# If we're uploading base files, don't send the email before the uploads, so # If we're uploading base files, don't send the email before the uploads, so
# that it contains the file status. # that it contains the file status.
if options.send_mail and options.download_base: if options.send_mail and options.download_base:
@ -1342,18 +1498,13 @@ def RealMain(argv, data=None):
uploaded_diff_file = [("data", "data.diff", data)] uploaded_diff_file = [("data", "data.diff", data)]
ctype, body = EncodeMultipartFormData(form_fields, uploaded_diff_file) ctype, body = EncodeMultipartFormData(form_fields, uploaded_diff_file)
response_body = rpc_server.Send("/upload", body, content_type=ctype) response_body = rpc_server.Send("/upload", body, content_type=ctype)
patchset = None
if not options.download_base or not uploaded_diff_file: if not options.download_base or not uploaded_diff_file:
lines = response_body.splitlines() lines = response_body.splitlines()
if len(lines) >= 2: if len(lines) >= 2:
msg = lines[0] msg = lines[0]
patchset = lines[1].strip() patchset = lines[1].strip()
patches = [x.split(" ", 1) for x in lines[2:]] patches = [x.split(" ", 1) for x in lines[2:]]
for patch_pair in patches:
# On Windows if a file has property changes its filename uses '\'
# instead of '/'. Perhaps this change should be made (also) on the
# server when it is decoding the patch file sent by the client, but
# we do it here as well to be safe.
patch_pair[1] = patch_pair[1].replace('\\', '/')
else: else:
msg = response_body msg = response_body
else: else:

Loading…
Cancel
Save